////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 2010.
// -------------------------------------------------------------------------
//  File name:   CryGameSpyDLCStore.cpp
//  Version:     v1.00
//  Created:     02/09/2010 by Paul Mikell.
//  Description: CCryGameSpyDLCStore member definitions
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////


#include "StdAfx.h"
#include "ICryLobby.h"
#include "CryGameSpyLobby.h"
#include "CryGameSpyDLCStore.h"


#if USE_CRYLOBBY_GAMESPY

#define GET_PURCHASE_HISTORY_NOT_STARTED								-1
#define GET_PURCHASE_HISTORY_STARTED										-2
#define GET_PURCHASE_HISTORY_FAILED											-3

#define COMMON_PURCHASE_HISTORY_PARAM										0

#define CATEGORY_GET_INFO_PARAM													1

#define CATEGORY_LIST_ITEMS_ID_PARAM										1
#define CATEGORY_LIST_ITEMS_ITEMS_PARAM									2

#define ITEM_GET_INFO_PARAM															1

#define DOWNLOADABLE_IS_DOWNLOADED_ID_PARAM							1
#define DOWNLOADABLE_IS_DOWNLOADED_RESULT_PARAM					2

#define USER_GET_PURCHASE_HISTORY_USER_PARAM						1

#define USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM	1
#define USER_LIST_PAYMENT_METHODS_STATE_PARAM						2

#define USER_LIST_PAYMENT_METHODS_STATE_NOT_STARTED			0
#define USER_LIST_PAYMENT_METHODS_STATE_IN_PROGRESS			1
#define USER_LIST_PAYMENT_METHODS_STATE_COMPLETE				2

#define USER_GET_DOWNLOADABLE_ACCESS_PARAM							1

#define USER_DOWNLOAD_ITEM_ID_PARAM											2
#define USER_DOWNLOAD_ITEM_STATE_PARAM									3
#define USER_DOWNLOAD_ITEM_PATH_PARAM										4

#define USER_DOWNLOAD_ITEM_NOT_STARTED									0
#define USER_DOWNLOAD_ITEM_STARTED											1
#define USER_DOWNLOAD_ITEM_NOT_NEEDED										2
#define USER_DOWNLOAD_ITEM_SUCCEEDED										3
#define USER_DOWNLOAD_ITEM_FAILED												4

#define USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM		5
#define USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM			6
#define USER_REDOWNLOAD_ALL_MISSING_ITEMS_STATE_PARAM		7
#define USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM		8

#define ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM			1
#define ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM				2

#define GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM					3
#define GET_ORDER_TOTAL_ERROR_PARAM											4

#define ORDER_GET_ITEM_INFO_PARAM												1

#define ORDER_POLL_DELAY		2.0F	// Seconds. Polling allowed to start 2 seconds after StartOrderCallback
#define ORDER_POLL_INTERVAL	5.0F	// Seconds. TCR ### requires minimum of 5 sedoncs

#define INVALID_FILE_ID			0

#define PREFERRED_CURRENCY_DECIMAL_PLACES	2


CCryGameSpyDLCStore::CCryGameSpyDLCStore( CCryLobby* pLobby, CCryLobbyService* pService )
:	CCryDLCStore( pLobby, pService ),
	m_pD2GInstance( NULL ),
	m_pD2GCatalog( NULL ),
	m_pD2GCatalogItemList( NULL ),
	m_pD2GDownloadRef( NULL )
{
	for ( uint32 i = 0; i < MAX_DLCSTORE_TASKS; ++i )
	{
		CCryDLCStore::m_pTasks[ i ] = &m_tasks[ i ];
	}

	for ( uint32 i = 0; i < MAX_DLCSTORE_ORDERS; ++i )
	{
		CCryDLCStore::m_pOrders[ i ] = &m_orders[ i ];
	}

	m_D2GCategoryList.mCount = 0;
	m_D2GCategoryList.mCategoryNames = NULL;
}


ECryLobbyError CCryGameSpyDLCStore::Initialise()
{
	ECryLobbyError	result = eCLE_Success;

	if ( result == eCLE_Success )
	{
		result = CCryDLCStore::Initialise();
	}
	
	return result;
}


ECryLobbyError CCryGameSpyDLCStore::Terminate()
{
	if ( m_D2GCategoryList.mCategoryNames )
	{
		gsifree( m_D2GCategoryList.mCategoryNames );
		m_D2GCategoryList.mCategoryNames = NULL;
		m_D2GCategoryList.mCount = 0;
	}

	if ( m_pD2GCatalogItemList )
	{
		d2gFreeCatalogItemList( m_pD2GCatalogItemList );
		m_pD2GCatalogItemList = NULL;
	}

	if ( m_pD2GCatalog )
	{
		d2gCleanupCatalog( m_pD2GInstance, m_pD2GCatalog );
		m_pD2GCatalog = NULL;
	}

	if ( m_pD2GInstance )
	{
		d2gCleanup( m_pD2GInstance );
		m_pD2GInstance = NULL;
	}

	return CCryDLCStore::Terminate();
}


void CCryGameSpyDLCStore::Tick( CTimeValue tv )
{
	if ( m_fromD2GDownloadThreadQueueMutex.TryLock() )
	{
		m_fromD2GDownloadThreadQueue.Flush( false );
		m_fromD2GDownloadThreadQueueMutex.Unlock();
	}

	// Tick tasks.

	for ( CryDLCStoreTaskID dlcsTaskID = 0; dlcsTaskID < MAX_DLCSTORE_TASKS; ++dlcsTaskID )
	{
		STask*	pTask = &m_tasks[ dlcsTaskID ];

		if ( pTask->used && pTask->running && ( pTask->error == eCLE_Success ) && !pTask->cancelled )
		{
			if ( ( !pTask->started ) && ( CheckTaskGameSpyObjects( dlcsTaskID ) == eTGSCR_Proceed ) )
			{
				switch ( pTask->startedTask )
				{
				case eT_CheckAvailability:
					StartCheckAvailability( dlcsTaskID );
					break;
				case eT_CatalogListCategories:
					StartCatalogListCategories( dlcsTaskID );
					break;
				case eT_CatalogListItems:
					StartCatalogListItems( dlcsTaskID );
					break;
				case eT_CategoryGetInfo:
					StartCategoryGetInfo( dlcsTaskID );
					break;
				case eT_CategoryListItems:
					StartCategoryListItems( dlcsTaskID );
					break;
				case eT_ItemGetInfo:
					StartItemGetInfo( dlcsTaskID );
					break;
				case eT_DownloadableIsDownloaded:
					StartDownloadableIsDownloaded( dlcsTaskID );
					break;
				case eT_UserGetPurchaseHistory:
					StartUserGetPurchaseHistory( dlcsTaskID );
					break;
				case eT_UserStartOrder:
					StartUserStartOrder( dlcsTaskID );
					break;
				case eT_UserGetDownloadableAccess:
					StartUserGetDownloadableAccess( dlcsTaskID );
					break;
				case eT_UserDownloadItem:
					StartUserDownloadItem( dlcsTaskID );
					break;
				case eT_UserRedownloadAllMissingItems:
					StartUserRedownloadAllMissingItems( dlcsTaskID );
					break;
				case eT_UserListPaymentMethods:
					StartUserListPaymentMethods( dlcsTaskID );
					break;
				case eT_OrderAdjustItemQuantity:
					StartOrderAdjustItemQuantity( dlcsTaskID );
					break;
				case eT_OrderFinalize:
					StartOrderFinalize( dlcsTaskID );
					break;
				case eT_OrderListItems:
					StartOrderListItems( dlcsTaskID );
					break;
				case eT_OrderGetInfo:
					StartOrderGetInfo( dlcsTaskID );
					break;
				case eT_OrderGetItemInfo:
					StartOrderGetItemInfo( dlcsTaskID );
					break;
				case eT_OrderSetPaymentMethod:
					StartOrderSetPaymentMethod( dlcsTaskID );
					break;
				case eT_OrderCancel:
					StartOrderCancel( dlcsTaskID );
					break;
				case eT_OrderFree:
					StartOrderFree( dlcsTaskID );
					break;
				}

				pTask->started = true;
			}

			if ( pTask->started )
			{
				switch ( pTask->subTask )
				{
				case eT_CheckAvailability:
					TickCheckAvailability( dlcsTaskID );
					break;
				case eT_CatalogListCategories:
					TickCatalogListCategories( dlcsTaskID );
					break;
				case eT_CatalogListItems:
					TickCatalogListItems( dlcsTaskID );
					break;
				case eT_CategoryGetInfo:
					TickCategoryGetInfo( dlcsTaskID );
					break;
				case eT_CategoryListItems:
					TickCategoryListItems( dlcsTaskID );
					break;
				case eT_ItemGetInfo:
					TickItemGetInfo( dlcsTaskID );
					break;
				case eT_UserGetPurchaseHistory:
					TickUserGetPurchaseHistory( dlcsTaskID );
					break;
				case eT_UserGetDownloadableAccess:
					TickUserGetDownloadableAccess( dlcsTaskID );
					break;
				case eT_UserDownloadItem:
					TickUserDownloadItem( dlcsTaskID );
					break;
				case eT_UserRedownloadAllMissingItems:
					TickUserRedownloadAllMissingItems( dlcsTaskID );
					break;
				case eT_UserListPaymentMethods:
					TickUserListPaymentMethods( dlcsTaskID );
					break;
				case eT_OrderFinalize:
					TickOrderFinalize( dlcsTaskID );
					break;
				case eT_OrderGetItemInfo:
					TickOrderGetItemInfo( dlcsTaskID );
					break;
				case eT_OrderSetPaymentMethod:
					TickOrderSetPaymentMethod( dlcsTaskID );
					break;
				case eT_OrderAdjustItemQuantity:
					// FALL THROUGH
				case eGST_RevertItemQuantity:
					TickOrderAdjustItemQuantity( dlcsTaskID );
					break;
				case eGST_GetOrderTotal:
					TickGetOrderTotal( dlcsTaskID );
					break;
				case eGST_RefreshCatalog:
					TickRefreshCatalog( dlcsTaskID );
					break;
				}
			}
		}

		if ( ( pTask->error != eCLE_Success ) || pTask->cancelled )
		{
			StopTaskRunning( dlcsTaskID );
		}
	}

	// Poll transactions in progress.

	for ( CryDLCOrderHandle h = 0; h < MAX_DLCSTORE_ORDERS; ++h )
	{
		SOrder*		pOrder = &m_orders[ h ];

		if ( pOrder->used && ( pOrder->transactionState == eTS_Started ) && !pOrder->polling && ( ( tv - pOrder->lastPollTime ).GetSeconds() > ORDER_POLL_INTERVAL ) )
		{
			if ( GS_SUCCEEDED( d2gIsOrderComplete( m_pD2GInstance, m_pD2GCatalog, pOrder->pPurchase, OrderCompleteCallback, pOrder->pLink ) ) )
			{
				PassOrderLinkToGameSpy( h );
				pOrder->polling = true;
			}
		}
	}

	CCryDLCStore::Tick( tv );
}


void CCryGameSpyDLCStore::StartTaskRunning( CryDLCStoreTaskID dlcsTaskID )
{
	LOBBY_AUTO_LOCK;

	CCryDLCStore::StartTaskRunning( dlcsTaskID );
}


void CCryGameSpyDLCStore::StopTaskRunning( CryDLCStoreTaskID dlcsTaskID )
{
	STask*		pTask = &m_tasks[ dlcsTaskID ];

	FreeTaskLinkFromCryNetwork( pTask );
	CCryDLCStore::StopTaskRunning( dlcsTaskID );
}


void CCryGameSpyDLCStore::EndTask( CryDLCStoreTaskID dlcsTaskID )
{
	LOBBY_AUTO_LOCK;

	STask* pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->used )
	{
		if ( pTask->pCb )
		{
			switch ( pTask->startedTask )
			{
			case eT_CheckAvailability:
				( ( CryDLCCheckAvailabilityCallback )pTask->pCb )( pTask->lTaskID, pTask->error, m_storeStatus, pTask->pCbArg );
				break;
			case eT_CatalogListCategories:
				EndCatalogListCategories( dlcsTaskID );
				break;
			case eT_CatalogListItems:
				EndCatalogListItems( dlcsTaskID );
				break;
			case eT_CategoryGetInfo:
				EndCategoryGetInfo( dlcsTaskID );
				break;
			case eT_CategoryListItems:
				EndCategoryListItems( dlcsTaskID );
				break;
			case eT_ItemGetInfo:
				EndItemGetInfo( dlcsTaskID );
				break;
			case eT_DownloadableIsDownloaded:
				( ( CryDLCDownloadableIsDownloadedCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->paramsNum[ DOWNLOADABLE_IS_DOWNLOADED_RESULT_PARAM ] == TRUE, pTask->pCbArg );
				break;
			case eT_UserGetPurchaseHistory:
				ReportPurchaseHistory( dlcsTaskID );
				break;
			case eT_UserStartOrder:
				( ( CryDLCCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->pCbArg );
				break;
			case eT_UserGetDownloadableAccess:
				EndUserGetDownloadableAccess( dlcsTaskID );
				break;
			case eT_UserDownloadItem:
				EndUserDownloadItem( dlcsTaskID );
				break;
			case eT_UserRedownloadAllMissingItems:
				EndUserRedownloadAllMissingItems( dlcsTaskID );
				break;
			case eT_UserListPaymentMethods:
				EndUserListPaymentMethods( dlcsTaskID );
				break;
			case eT_OrderFinalize:
				ReportPurchaseHistory( dlcsTaskID );
				break;
			case eT_OrderAdjustItemQuantity:
				( ( CryDLCCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->pCbArg );
				break;
			case eT_OrderListItems:
				EndOrderListItems( dlcsTaskID );
				break;
			case eT_OrderGetInfo:
				EndOrderGetInfo( dlcsTaskID );
				break;
			case eT_OrderGetItemInfo:
				EndOrderGetItemInfo( dlcsTaskID );
				break;
			case eT_OrderSetPaymentMethod:
				( ( CryDLCCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->pCbArg );
				break;
			case eT_OrderCancel:
				( ( CryDLCCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->pCbArg );
				break;
			case eT_OrderFree:
				( ( CryDLCCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pTask->pCbArg );
				break;
			}
		}

		if ( pTask->error != eCLE_Success )
		{
			NetLog( "[Lobby] CCryGameSpyDLCStore::EndTask() %d (%d) Result %d", pTask->startedTask, pTask->subTask, pTask->error );
		}
	}

	CCryDLCStore::EndTask( dlcsTaskID );
}


void CCryGameSpyDLCStore::FreeTask( CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = &m_tasks[ dlcsTaskID ];

	SPurchaseHistoryItem*	pParam = static_cast< SPurchaseHistoryItem* >( m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );

	if ( pParam )
	{
		uint32								count = pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ];

		for ( uint32 i = 0; i < count; ++i )
		{
			if ( pParam[ i ].type == ePHIT_License )
			{
				SAFE_DELETE_ARRAY( pParam[ i ].data.license.name );
				SAFE_DELETE_ARRAY( pParam[ i ].data.license.key );
			}
		}
	}

	switch ( pTask->startedTask  )
	{
	case eT_UserListPaymentMethods:

		{
			SCryDLCPaymentMethodInfo*	pParam = static_cast< SCryDLCPaymentMethodInfo* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ] ) );

			if ( pParam )
			{
				uint32								count = pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ];

				for ( uint32 i = 0; i < count; ++i )
				{
					pParam[ i ].~SCryDLCPaymentMethodInfo();
				}
			}
		}

		break;

	case eT_OrderAdjustItemQuantity:
		// FALL THROUGH
	case eT_OrderSetPaymentMethod:

		{
			CryDLCPaymentMethodID*		pParam = static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) );

			if ( pParam )
			{
				pParam->~CryDLCPaymentMethodID();
			}
		}

		break;
	}

	CCryDLCStore::FreeTask( dlcsTaskID );
}


void CCryGameSpyDLCStore::ClearTask( CryDLCStoreTaskID dlcsTaskID )
{
	STask* pTask = &m_tasks[ dlcsTaskID ];

	assert( pTask );

	pTask->pLink = NULL;
	pTask->oldGameSpyObjects = 0;
	pTask->newGameSpyObjects = 0;
	pTask->haveGameSpyObjects = 0;
	pTask->started = false;

	CCryDLCStore::ClearTask( dlcsTaskID );
}


ECryLobbyError CCryGameSpyDLCStore::CheckAvailability( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCCheckAvailabilityCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_CheckAvailability, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start SessionJoin error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartCheckAvailability( CryDLCStoreTaskID dlcsTaskID )
{
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( EnsureD2GInitialised( dlcsTaskID ) )
	{
		if ( ( m_storeStatus != eCDLCSS_Querying ) && ( m_storeStatus != eCDLCSS_QueryFailed ) )
		{
			if ( GS_SUCCEEDED( d2gGetStoreAvailability( m_pD2GInstance, m_pD2GCatalog, GetStoreAvailabilityCallback, this ) ) )
			{
				AddRef();
				m_storeStatus = eCDLCSS_Querying;
			}
			else
			{
				m_storeStatus = eCDLCSS_Unknown;
				UpdateTaskError( dlcsTaskID, eCLE_InternalError );
			}
		}
	}
}


void CCryGameSpyDLCStore::TickCheckAvailability( CryDLCStoreTaskID dlcsTaskID )
{
	if ( m_storeStatus != eCDLCSS_Querying )
	{
		if ( m_storeStatus == eCDLCSS_QueryFailed )
		{
			m_storeStatus = eCDLCSS_Unknown;
			UpdateTaskError( dlcsTaskID, eCLE_InternalError );
		}

		StopTaskRunning( dlcsTaskID );
	}
}


ECryLobbyError CCryGameSpyDLCStore::CatalogListCategories( CryLobbyTaskID* pTaskID, CryDLCListCategoriesCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_CatalogListCategories, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start SessionJoin error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartCatalogListCategories( CryDLCStoreTaskID dlcsTaskID )
{
	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickCatalogListCategories( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndCatalogListCategories( CryDLCStoreTaskID dlcsTaskID )
{
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->error == eCLE_Success )
	{
		if( m_catalogCategories.size() )
		{
			uint32	last = m_catalogCategories.size() - 1;

			for ( uint32 i = 0; i < last; ++i )
			{
				( ( CryDLCListCategoriesCallback )pTask->pCb )( pTask->lTaskID, eCLE_SuccessContinue, &m_catalogCategories[ i ], pTask->pCbArg );
			}

			( ( CryDLCListCategoriesCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, &m_catalogCategories[ last ], pTask->pCbArg );
		}
		else
		{
			( ( CryDLCListCategoriesCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, NULL, pTask->pCbArg );
		}
	}
	else
	{
		( ( CryDLCListCategoriesCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::CatalogListItems( CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_CatalogListItems, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start SessionJoin error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartCatalogListItems( CryDLCStoreTaskID dlcsTaskID )
{
	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickCatalogListItems( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndCatalogListItems( CryDLCStoreTaskID dlcsTaskID )
{
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->error == eCLE_Success )
	{
		if( m_catalogItems.size() )
		{
			uint32	last = m_catalogItems.size() - 1;

			for ( uint32 i = 0; i < last; ++i )
			{
				( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_SuccessContinue, &m_catalogItems[ i ], pTask->pCbArg );
			}

			( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, &m_catalogItems[ last ], pTask->pCbArg );
		}
		else
		{
			( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, NULL, pTask->pCbArg );
		}
	}
	else
	{
		( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::CategoryGetInfo( CryDLCCategoryID categoryID, CryLobbyTaskID* pTaskID, CryDLCListCategoriesCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_CategoryGetInfo, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		SCryGameSpyDLCCategoryID*	pID = static_cast< SCryGameSpyDLCCategoryID* >( categoryID.get() );

		error = CreateTaskParamMem( dlcsTaskID, CATEGORY_GET_INFO_PARAM, NULL, sizeof ( int32 ) + ( pID->name.length() + 1 ) * sizeof ( wchar_t ) );

		if ( error == eCLE_Success )
		{
			STask*										pTask = &m_tasks[ dlcsTaskID ];
			SCategoryGetInfoParam*		pParam = static_cast< SCategoryGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_GET_INFO_PARAM ] ) );

			pParam->index = -1;
			wcscpy( pParam->name, pID->name.c_str() );
			InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
			FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
		}

		if ( error != eCLE_Success )
		{
			FreeTask( dlcsTaskID );
		}
	}

	CryLogAlways( "[Lobby] Start SessionJoin error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		STask*									pTask = &m_tasks[ dlcsTaskID ];
		SCategoryGetInfoParam*	pParam = static_cast< SCategoryGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_GET_INFO_PARAM ] ) );

		for ( int32 i = 0; ( i < m_catalogCategories.size() ) && ( pParam->index == -1 ); ++i )
		{
			if ( !wcscmp( static_cast< SCryGameSpyDLCCategoryID* >( m_catalogCategories[ i ].id.get() )->name.c_str(), pParam->name ) )
			{
				pParam->index = i;
			}
		}

		if ( pParam->index == -1 )
		{
			UpdateTaskError( dlcsTaskID, eCLE_InternalError );
		}

		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	STask*									pTask = &m_tasks[ dlcsTaskID ];
	SCategoryGetInfoParam*	pParam = static_cast< SCategoryGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_GET_INFO_PARAM ] ) );
	SCryDLCCategoryInfo*		pCategoryInfo;

	if ( pParam->index == -1 )
	{
		pCategoryInfo = NULL;
	}
	else
	{
		pCategoryInfo = &m_catalogCategories[ pParam->index ];
	}

	( ( CryDLCListCategoriesCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pCategoryInfo, pTask->pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::CategoryListItems( CryDLCCategoryID categoryID, CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_CategoryListItems, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		SCryGameSpyDLCCategoryID*	pID = static_cast< SCryGameSpyDLCCategoryID* >( categoryID.get() );

		error = CreateTaskParamMem( dlcsTaskID, CATEGORY_LIST_ITEMS_ID_PARAM, pID->name.c_str(), ( pID->name.length() + 1 ) * sizeof ( wchar_t ) );

		if ( error == eCLE_Success )
		{
			STask*						pTask = &m_tasks[ dlcsTaskID ];

			InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
			FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
		}

		if ( error != eCLE_Success )
		{
			FreeTask( dlcsTaskID );
		}
	}

	CryLogAlways( "[Lobby] Start SessionJoin error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartCategoryListItems( CryDLCStoreTaskID dlcsTaskID )
{
	EnsureD2GInitialised( dlcsTaskID );

	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickCategoryListItems( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];
		uint32*						pItemIndices;
		ECryLobbyError		error = GetCategoryItems( dlcsTaskID, static_cast< wchar_t* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_LIST_ITEMS_ID_PARAM ] ) ), 0, 0, pItemIndices );

		if ( error != eCLE_Success )
		{
			UpdateTaskError( dlcsTaskID, error );
		}

		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndCategoryListItems( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->error == eCLE_Success )
	{
		uint32*								pItemIndices = static_cast< uint32* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_LIST_ITEMS_ITEMS_PARAM ] ) );
		int32									last = pTask->paramsNum[ CATEGORY_LIST_ITEMS_ITEMS_PARAM ] - 1;

		if ( last > -1 )
		{
			for ( int32 i = 0; i < last; ++i )
			{
				( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_SuccessContinue, &m_catalogItems[ pItemIndices[ i ] ], pTask->pCbArg );
			}

			( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, &m_catalogItems[ pItemIndices[ last ] ], pTask->pCbArg );
		}
	}
	else
	{
		( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::GetCategoryItems( CryDLCStoreTaskID dlcsTaskID, wchar_t* pName, uint32 i, uint32 count, uint32*& pItemIndex )
{
	ECryLobbyError	error;

	while ( i < m_catalogItems.size() )
	{
		uint32					j;

		for ( j = 0; j < m_catalogItems[ i ].categories.size(); ++j )
		{
			if ( !wcscmp( static_cast< SCryGameSpyDLCCategoryID* >( m_catalogItems[ i ].categories[ j ].get() )->name.c_str(), pName ) )
			{
				break;
			}
		}

		if ( j < m_catalogItems[ i ].categories.size() )
		{
			break;
		}

		++i;
	}

	if ( i == m_catalogItems.size() )
	{
		error = CreateTaskParamMem( dlcsTaskID, CATEGORY_LIST_ITEMS_ITEMS_PARAM, NULL, count * sizeof ( uint32 ) );

		if ( error == eCLE_Success )
		{
			STask*						pTask = &m_tasks[ dlcsTaskID ];

			pTask->paramsNum[ CATEGORY_LIST_ITEMS_ITEMS_PARAM ] = count;
			pItemIndex = static_cast< uint32* >( m_pLobby->MemGetPtr( pTask->paramsMem[ CATEGORY_LIST_ITEMS_ITEMS_PARAM ] ) );
			pItemIndex += count;
		}
	}
	else
	{
		error = GetCategoryItems( dlcsTaskID, pName, i + 1, count + 1, pItemIndex );

		if ( error == eCLE_Success )
		{
			--pItemIndex;
			*pItemIndex = i;
		}
	}

	return error;
}


ECryLobbyError CCryGameSpyDLCStore::ItemGetInfo( CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_ItemGetInfo, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		SCryGameSpyDLCItemID*	pID = static_cast< SCryGameSpyDLCItemID* >( itemID.get() );
		SItemGetInfoParam			param;

		param.itemID = pID->itemID;
		param.index = -1;

		error = CreateTaskParamMem( dlcsTaskID, ITEM_GET_INFO_PARAM, &param, sizeof ( SItemGetInfoParam ) );

		if ( error == eCLE_Success )
		{
			STask*						pTask = &m_tasks[ dlcsTaskID ];

			InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
			FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
		}

		if ( error != eCLE_Success )
		{
			FreeTask( dlcsTaskID );
		}
	}

	CryLogAlways( "[Lobby] Start ItemGetInfo error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartItemGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickItemGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		STask*							pTask = &m_tasks[ dlcsTaskID ];
		SItemGetInfoParam*	pParam = static_cast< SItemGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ ITEM_GET_INFO_PARAM ] ) );

		for ( int32 i = 0; ( i < m_catalogItems.size() ) && ( pParam->index == -1 ); ++i )
		{
			if ( static_cast< SCryGameSpyDLCItemID* >( m_catalogItems[ i ].id.get() )->itemID == pParam->itemID )
			{
				pParam->index = i;
			}
		}

		if ( pParam->index == -1 )
		{
			UpdateTaskError( dlcsTaskID, eCLE_InternalError );
		}

		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndItemGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SItemGetInfoParam*		pParam = static_cast< SItemGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ ITEM_GET_INFO_PARAM ] ) );
	SCryDLCItemInfo*			pItemInfo;

	if ( pParam->index == -1 )
	{
		pItemInfo = NULL;
	}
	else
	{
		pItemInfo = &m_catalogItems[ pParam->index ];
	}

	( ( CryDLCListItemsCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pItemInfo, pTask->pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::DownloadableIsDownloaded( CryDLCDownloadableID downloadableID, const char* pDstPath, CryLobbyTaskID* pTaskID, CryDLCDownloadableIsDownloadedCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_DownloadableIsDownloaded, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ DOWNLOADABLE_IS_DOWNLOADED_ID_PARAM ] = static_cast< SCryGameSpyDLCDownloadableID* >( downloadableID.get() )->fileID;
		pTask->paramsNum[ DOWNLOADABLE_IS_DOWNLOADED_RESULT_PARAM ] = FALSE;
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start DownloadableIsDownloaded error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartDownloadableIsDownloaded( CryDLCStoreTaskID dlcsTaskID )
{
	STask*											pTask = &m_tasks[ dlcsTaskID ];
	CryFixedStringT< MAX_PATH >	path;
	bool												downloaded = false;
	std::vector< SCryDLCDownloadableInfo >::iterator it;

	for ( it = m_downloadables.begin(); it < m_downloadables.end(); ++it )
	{
		if ( static_cast< SCryGameSpyDLCDownloadableID* >( it->id.get() )->fileID == pTask->paramsNum[ DOWNLOADABLE_IS_DOWNLOADED_ID_PARAM ] )
		{
			break;
		}
	}

	if ( it < m_downloadables.end() )
	{
		m_downloadableVerifyCallback( *it, &path, &downloaded );
	}

	if ( downloaded )
	{
		pTask->paramsNum[ DOWNLOADABLE_IS_DOWNLOADED_RESULT_PARAM ] = true;
	}

	StopTaskRunning( dlcsTaskID );
}


ECryLobbyError CCryGameSpyDLCStore::UserGetPurchaseHistory( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCOrderFinalizeCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	if( userID != CryUserInvalidID )
	{
		error = StartTaskWithNoOrder( eT_UserGetPurchaseHistory, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

		if ( error == eCLE_Success )
		{
			STask*								pTask = &m_tasks[ dlcsTaskID ];

			pTask->paramsNum[ USER_GET_PURCHASE_HISTORY_USER_PARAM ] = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;
			pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = GET_PURCHASE_HISTORY_NOT_STARTED;
			InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
			FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
		}
	}
	else
	{
		error = eCLE_InvalidUser;
	}

	CryLogAlways( "[Lobby] Start UserGetPurchaseHistory error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserGetPurchaseHistory( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( CreateTaskLink( dlcsTaskID ) )
	{
		EnsureD2GInitialised( dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickUserGetPurchaseHistory( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	switch ( pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] )
	{
	case GET_PURCHASE_HISTORY_NOT_STARTED:

		if ( GS_SUCCEEDED( d2gGetPurchaseHistory( m_pD2GInstance, m_pD2GCatalog, GetPurchaseHistoryCallback, pTask->pLink ) ) )
		{
			PassTaskLinkToGameSpy( dlcsTaskID );
			pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = GET_PURCHASE_HISTORY_STARTED;
		}
		else
		{
			UpdateTaskError( pTask, eCLE_InternalError );
		}

		break;

	case GET_PURCHASE_HISTORY_STARTED:

		break;

	case GET_PURCHASE_HISTORY_FAILED:

		UpdateTaskError( pTask, eCLE_InternalError );
		break;

	default:

		if ( pTask->startedTask != eT_UserGetPurchaseHistory )
		{
			StartSubTask( pTask->startedTask, dlcsTaskID );
		}
		else
		{
			StopTaskRunning( dlcsTaskID );
		}

		break;
	}
}


ECryLobbyError CCryGameSpyDLCStore::UserStartOrder( CryUserID userID, CryDLCOrderHandle* pOrderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNewOrder( eT_UserStartOrder, &dlcsTaskID, pTaskID, userID, pOrderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start UserStartOrder error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserStartOrder( CryDLCStoreTaskID dlcsTaskID )
{
	StopTaskRunning( dlcsTaskID );
}


ECryLobbyError CCryGameSpyDLCStore::UserGetDownloadableAccess( CryUserID userID, CryDLCDownloadableID downloadableID, CryLobbyTaskID* pTaskID, CryDLCUserGetDownloadableAccessCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_UserGetDownloadableAccess, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = GET_PURCHASE_HISTORY_NOT_STARTED;
		pTask->paramsNum[ USER_GET_DOWNLOADABLE_ACCESS_PARAM ] = static_cast< SCryGameSpyDLCDownloadableID* >( downloadableID.get() )->fileID;
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start UserGetDownloadableAccess error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( CreateTaskLink( dlcsTaskID ) )
	{
		if ( EnsureD2GInitialised( dlcsTaskID ) )
		{
			StartSubTask( eT_UserGetPurchaseHistory, dlcsTaskID );
		}
	}
}


void CCryGameSpyDLCStore::TickUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID )
{
	StopTaskRunning( dlcsTaskID );
}


void CCryGameSpyDLCStore::EndUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SPurchaseHistoryItem*	pPurchaseHistory = static_cast< SPurchaseHistoryItem* >( m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );
	EDownloadableAccess		access = eDA_None;

	if ( pTask->error == eCLE_Success )
	{
		for ( uint32 i = 0; i < pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ]; ++i )
		{
			if ( ( pPurchaseHistory[ i ].type == ePHIT_Downloadable ) && ( pPurchaseHistory[ i ].data.downloadable.fileID == pTask->paramsNum[ USER_GET_DOWNLOADABLE_ACCESS_PARAM ] ) )
			{
				access = eDA_Full;
				break;
			}
		}
	}

	( ( CryDLCUserGetDownloadableAccessCallback )pTask->pCb )( pTask->lTaskID, pTask->error, access, pTask->pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::UserDownloadItem( CryUserID userID, CryDLCDownloadableID downloadableID, CryLobbyTaskID* pTaskID, CryDLCUserDownloadItemCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_UserDownloadItem, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] = static_cast< SCryGameSpyDLCDownloadableID* >( downloadableID.get() )->fileID;
		pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_NOT_STARTED;
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start UserDownloadItem error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserDownloadItem( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( CreateTaskLink( dlcsTaskID ) )
	{
		EnsureD2GInitialised( dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickUserDownloadItem( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] == USER_DOWNLOAD_ITEM_NOT_STARTED )
	{
		std::vector< SCryDLCDownloadableInfo >::iterator it;

		for ( it = m_downloadables.begin(); it < m_downloadables.end(); ++it )
		{
			if ( static_cast< SCryGameSpyDLCDownloadableID* >( it->id.get() )->fileID == pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] )
			{
				break;
			}
		}

		if ( it < m_downloadables.end() )
		{
			bool													valid;
			CryFixedStringT< _MAX_PATH >	path;

			m_downloadableVerifyCallback( *it, &path, &valid );

			if ( !valid )
			{
				pTask->pLink->m_downloadPath = path;

				if ( GS_SUCCEEDED( d2gStartDownloadFileById( m_pD2GInstance, m_pD2GCatalog, pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ], pTask->pLink->m_downloadPath.c_str(),
																											gsi_false, &m_pD2GDownloadRef, DownloadProgressCallback, DownloadCompleteCallback, pTask->pLink ) ) )
				{
					PassTaskLinkToGameSpy( dlcsTaskID );
					pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_STARTED;
				}
				else
				{
					pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_FAILED;
				}
			}
			else
			{
				pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_NOT_NEEDED;
			}
		}
		else
		{
			pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_FAILED;
		}
	}

	if ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] != USER_DOWNLOAD_ITEM_STARTED )
	{
		if ( pTask->startedTask != eT_UserDownloadItem )
		{
			StartSubTask( pTask->startedTask, pTask );
		}
		else
		{
			if ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] == USER_DOWNLOAD_ITEM_FAILED )
			{
				UpdateTaskError( pTask, eCLE_InternalError );
			}

			StopTaskRunning( dlcsTaskID );
		}
	}
}


void CCryGameSpyDLCStore::EndUserDownloadItem( CryDLCStoreTaskID dlcsTaskID )
{
	STask*													pTask = &m_tasks[ dlcsTaskID ];
	SCryGameSpyDLCDownloadableID*		pID = new SCryGameSpyDLCDownloadableID( pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] );
	SDispatchDownloadCompleteParam	param;

	if ( !pID )
	{
		UpdateTaskError( pTask, eCLE_OutOfMemory );
	}

	param.cb = ( CryDLCUserDownloadItemCallback )pTask->pCb;
	param.complete = pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] == USER_DOWNLOAD_ITEM_SUCCEEDED;
	param.error = pTask->error;
	param.id = pID;
	param.lTaskID = pTask->lTaskID;
	param.path = static_cast< char* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] ) );
	param.pCbArg = pTask->pCbArg;

	DispatchDownloadComplete( param );
}

void CCryGameSpyDLCStore::CancelActiveDownloadInternal()
{
	d2gCancelDownload( m_pD2GDownloadRef );

}

ECryLobbyError CCryGameSpyDLCStore::UserCancelActiveDownload( CryUserID userID )
{
	ECryLobbyError retVal;
	if( m_pD2GDownloadRef )
	{
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::CancelActiveDownloadInternal, this );
		retVal = eCLE_Success;
	}
	else
	{
		retVal = eCLE_InternalError;
	}

	return retVal;
}



void CCryGameSpyDLCStore::DispatchDownloadComplete( SDispatchDownloadCompleteParam param )
{
	param.cb( param.lTaskID, param.error, param.id, param.complete, 0, 0, param.path.c_str(), param.pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::UserRedownloadAllMissingItems( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCUserDownloadItemCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_UserRedownloadAllMissingItems, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM ] = -1;
		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] = INVALID_FILE_ID;
		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_STATE_PARAM ] = USER_DOWNLOAD_ITEM_NOT_NEEDED;
		pTask->paramsNum[ USER_GET_PURCHASE_HISTORY_USER_PARAM ] = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;
		pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = GET_PURCHASE_HISTORY_NOT_STARTED;
		pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] = INVALID_FILE_ID;
		pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_NOT_NEEDED;
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start UserRedownloadAllMissingItems error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID )
{
	if ( CreateTaskLink( dlcsTaskID ) )
	{
		if ( EnsureD2GInitialised( dlcsTaskID ) )
		{
			StartSubTask( eT_UserGetPurchaseHistory, dlcsTaskID );
		}
	}
}


void CCryGameSpyDLCStore::TickUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] != INVALID_FILE_ID ) && ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] != USER_DOWNLOAD_ITEM_NOT_NEEDED ) )
	{
		if ( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] != INVALID_FILE_ID )
		{
			SCryGameSpyDLCDownloadableID*	pID = new	SCryGameSpyDLCDownloadableID( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] );

			if ( !pID )
			{
				UpdateTaskError( pTask, eCLE_OutOfMemory );
			}

			ECryLobbyError				error;

			if ( pTask->error == eCLE_Success )
			{
				error = eCLE_SuccessContinue;
			}
			else
			{
				error = pTask->error;
			}
			
			char*													pPath = static_cast< char* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ] ) );
			SDispatchDownloadCompleteParam	param;

			param.cb = ( CryDLCUserDownloadItemCallback )pTask->pCb;
			param.complete = pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_STATE_PARAM ] == USER_DOWNLOAD_ITEM_SUCCEEDED;
			param.error = pTask->error;
			param.id = pID;
			param.lTaskID = pTask->lTaskID;
			param.path = static_cast< char* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ] ) );
			param.pCbArg = pTask->pCbArg;

			TO_GAME_FROM_LOBBY( &CCryGameSpyDLCStore::DispatchDownloadComplete, this, param );
		}

		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] = pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ];
		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_STATE_PARAM ] = pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ];

		TMemHdl								tmpMemHdl;
		int32									tmpNum;

		tmpMemHdl = pTask->paramsMem[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ];
		pTask->paramsMem[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ] = pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ];
		pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] = tmpMemHdl;
		tmpNum = pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ];
		pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ] = pTask->paramsNum[ USER_DOWNLOAD_ITEM_PATH_PARAM ];
		pTask->paramsNum[ USER_DOWNLOAD_ITEM_PATH_PARAM ] = tmpNum;

		if ( pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] == USER_DOWNLOAD_ITEM_FAILED )
		{
			UpdateTaskError( pTask, eCLE_InternalError );
		}
	}

	if ( pTask->error == eCLE_Success )
	{
		SPurchaseHistoryItem*	pPurchaseHistory = static_cast< SPurchaseHistoryItem* >( m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );

		while ( ++( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM ] ) < pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] )
		{
			if ( pPurchaseHistory[ pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM ] ].type == ePHIT_Downloadable )
			{
				pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] = pPurchaseHistory[ pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM ] ].data.downloadable.fileID;
				pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_NOT_STARTED;
				StartSubTask( eT_UserDownloadItem, dlcsTaskID );
				break;
			}
		}
	}

	if ( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_INDEX_PARAM ] == pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] )
	{
		StopTaskRunning( dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::EndUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID )
{
	STask*													pTask = &m_tasks[ dlcsTaskID ];
	SCryGameSpyDLCDownloadableID*		pID = NULL;
	SDispatchDownloadCompleteParam	param;

	if ( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] != INVALID_FILE_ID )
	{
		pID = new SCryGameSpyDLCDownloadableID( pTask->paramsNum[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_ID_PARAM ] );

		if ( !pID )
		{
			UpdateTaskError( pTask, eCLE_OutOfMemory );
		}
	}

	param.cb = ( CryDLCUserDownloadItemCallback )pTask->pCb;
	param.complete = pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] == USER_DOWNLOAD_ITEM_SUCCEEDED;
	param.error = pTask->error;
	param.id = pID;
	param.lTaskID = pTask->lTaskID;
	param.path = static_cast< char* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_REDOWNLOAD_ALL_MISSING_ITEMS_PATH_PARAM ] ) );
	param.pCbArg = pTask->pCbArg;

	DispatchDownloadComplete( param );
}


ECryLobbyError CCryGameSpyDLCStore::UserListPaymentMethods( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCListPaymentMethodsCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithNoOrder( eT_UserListPaymentMethods, &dlcsTaskID, pTaskID, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_STATE_PARAM ] = USER_LIST_PAYMENT_METHODS_STATE_NOT_STARTED;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start UserStartOrder error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( CreateTaskLink( dlcsTaskID ) )
	{
		GSResult		result = d2gGetUserCreditCards( m_pD2GInstance, m_pD2GCatalog, true, GetUserCreditCardsCallback, pTask->pLink );

		if ( GS_SUCCEEDED( result ) )
		{
			pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_STATE_PARAM ] = USER_LIST_PAYMENT_METHODS_STATE_IN_PROGRESS;
			PassTaskLinkToGameSpy( dlcsTaskID );
		}
		else
		{
			UpdateTaskError( dlcsTaskID, eCLE_InternalError );
		}
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
	}
}


void CCryGameSpyDLCStore::TickUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_STATE_PARAM ] != USER_LIST_PAYMENT_METHODS_STATE_IN_PROGRESS )
	{
		StopTaskRunning( dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::EndUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID )
{
	STask*										pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->error == eCLE_Success )
	{
		SCryDLCPaymentMethodInfo*	pInfo = static_cast< SCryDLCPaymentMethodInfo* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ] ) );
		uint32										last = pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ];

		if ( last )
		{
			--last;
		}

		for ( uint32 i = 0; i < last; ++i )
		{
			( ( CryDLCListPaymentMethodsCallback )pTask->pCb )( pTask->lTaskID, eCLE_SuccessContinue, &pInfo[ i ], pTask->pCbArg );
		}

		( ( CryDLCListPaymentMethodsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, &pInfo[ last ], pTask->pCbArg );
	}
	else
	{
		( ( CryDLCListPaymentMethodsCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::OrderFinalize( CryDLCOrderHandle orderHandle, bool freeOnCompletion, CryLobbyTaskID* pTaskID, CryDLCOrderFinalizeCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderFinalize, &dlcsTaskID, pTaskID, orderHandle, cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*								pTask = &m_tasks[ dlcsTaskID ];
		SOrder*								pOrder = &m_orders[ pTask->order ];

		pOrder->transactionState = eTS_AboutToStart;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	return error;
}


void CCryGameSpyDLCStore::StartOrderFinalize( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SOrder*								pOrder = &m_orders[ pTask->order ];

	if ( pOrder->itemQuantities.size() && pOrder->paymentMethodID && pOrder->pTotal && ( pOrder->transactionState == eTS_AboutToStart ) )
	{
		if ( CreateOrderLink( pTask->order ) )
		{
			SOrder*								pOrder = &m_orders[ pTask->order ];

			if ( GS_SUCCEEDED( d2gStartOrder( m_pD2GInstance, m_pD2GCatalog, pOrder->pTotal, StartOrderCallback, pOrder->pLink ) ) )
			{
				PassOrderLinkToGameSpy( pTask->order );
				pOrder->transactionState = eTS_Starting;
			}
			else
			{
				UpdateTaskError( pTask, eCLE_InternalError );
			}
		}
	}
	else
	{
		UpdateTaskError( pTask, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::TickOrderFinalize( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SOrder*								pOrder = &m_orders[ pTask->order ];
	ECryLobbyError				error;

	switch ( pOrder->transactionState )
	{
	case eTS_Succeeded:

		{
			uint32	count = CountItemsInOrderPurchase( pOrder->pPurchase );

			error = CreateTaskParamMem( dlcsTaskID, COMMON_PURCHASE_HISTORY_PARAM, NULL, sizeof ( SPurchaseHistoryItem ) * count );

			if ( error == eCLE_Success )
			{
				SPurchaseHistoryItem*	pItem = static_cast< SPurchaseHistoryItem* >( m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );

				pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = count;
				WriteItemsInOrderPurchase( pTask, pOrder->pPurchase, pItem );
			}
			else
			{
				UpdateTaskError( pTask, error );
			}

			StopTaskRunning( dlcsTaskID );
		}

		break;

	case eTS_Failed:

		UpdateTaskError( pTask, eCLE_InternalError );
		break;
	}
}


void CCryGameSpyDLCStore::ReportPurchaseHistory( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SPurchaseHistoryItem*	pItem = static_cast< SPurchaseHistoryItem* >( m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );
	SCryGameSpyDLCItemID*	pItemID = NULL;
	SCryGameSpyDLCDownloadableID* pDownloadableID;
	SCryGameSpyDLCLicense*	pLicense;
	ECryLobbyError				success = eCLE_SuccessContinue;

	for ( uint32 i = 0; ( i < pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] ) && ( pTask->error == eCLE_Success ); ++i )
	{
		if ( i == pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] - 1 )
		{
			success = eCLE_Success;
		}

		switch ( pItem[ i ].type )
		{
		case ePHIT_Purchasable:

			pItemID = new SCryGameSpyDLCItemID();

			if ( pItemID )
			{
				pItemID->itemID = pItem[ i ].data.purchasable.itemID;
				( ( CryDLCOrderFinalizeCallback )pTask->pCb )( pTask->lTaskID, success, pItemID, NULL, NULL, pTask->pCbArg );
			}
			else
			{
				UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
			}

			break;

		case ePHIT_Downloadable:

			pDownloadableID = new SCryGameSpyDLCDownloadableID();

			if ( pDownloadableID )
			{
				pDownloadableID->fileID = pItem[ i ].data.downloadable.fileID;
				( ( CryDLCOrderFinalizeCallback )pTask->pCb )( pTask->lTaskID, success, NULL, pDownloadableID, NULL, pTask->pCbArg );
			}
			else
			{
				UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
			}

			break;

		case ePHIT_License:

			pLicense = new SCryGameSpyDLCLicense();

			if ( pLicense )
			{
				pLicense->name = reinterpret_cast< wchar_t* >( pItem[ i ].data.license.name );
				pLicense->key = reinterpret_cast< wchar_t* >( pItem[ i ].data.license.key );
				( ( CryDLCOrderFinalizeCallback )pTask->pCb )( pTask->lTaskID, success, NULL, NULL, pLicense, pTask->pCbArg );
			}
			else
			{
				UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
			}

			break;
		}
	}

	if ( ( pTask->error != eCLE_Success ) || ( pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] == 0 ) )
	{
		( ( CryDLCOrderFinalizeCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, NULL, NULL, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::OrderAddItem( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderAdjustItemQuantity, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ] = static_cast< SCryGameSpyDLCItemID* >( itemID.get() )->itemID;
		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ] = 1;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderAddItem error %d", error );

	return error;
}


ECryLobbyError CCryGameSpyDLCStore::OrderRemoveItem( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderAdjustItemQuantity, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ] = static_cast< SCryGameSpyDLCItemID* >( itemID.get() )->itemID;
		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ] = -1;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderRemoveItem error %d", error );

	return error;
}


ECryLobbyError CCryGameSpyDLCStore::OrderAddItemQuantity( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, uint32 quantity, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderAdjustItemQuantity, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ] = static_cast< SCryGameSpyDLCItemID* >( itemID.get() )->itemID;
		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ] = quantity;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderAddItemQuantity error %d", error );

	return error;
}


ECryLobbyError CCryGameSpyDLCStore::OrderRemoveItemQuantity( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, uint32 quantity, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderAdjustItemQuantity, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];

		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ] = static_cast< SCryGameSpyDLCItemID* >( itemID.get() )->itemID;
		pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ] = 0 - quantity;
		InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderRemoveItemQuantity error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderAdjustItemQuantity( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];

	if ( pOrder->transactionState != eTS_NotStarted )
	{
		UpdateTaskError( pTask, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::TickOrderAdjustItemQuantity( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];

	if ( ( pTask->subTask == eGST_RevertItemQuantity ) || ( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] == TMemInvalidHdl ) )
	{
		SCryGameSpyDLCItemID*	pItemID;
		uint32						i;
		int32							oldQuantity;
		int32							newQuantity;

		for ( i = 0; i < pOrder->itemQuantities.size(); ++i )
		{
			if ( static_cast< SCryGameSpyDLCItemID* >( pOrder->itemQuantities[ i ].itemID.get() )->itemID == pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ] )
			{
				break;
			}
		}

		if ( i < pOrder->itemQuantities.size() )
		{
			oldQuantity = pOrder->itemQuantities[ i ].quantity;
		}
		else
		{
			oldQuantity = 0;
		}

		newQuantity = oldQuantity + pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ];

		if ( newQuantity >= 0 )
		{
			if ( newQuantity > 0 )
			{
				if ( i == pOrder->itemQuantities.size() )
				{
					SItemQuantity			itemQuantity;

					pItemID = new SCryGameSpyDLCItemID();

					if ( pItemID )
					{
						pItemID->itemID = pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_D2GITEMID_PARAM ];
						itemQuantity.itemID = pItemID;
						pOrder->itemQuantities.push_back( itemQuantity );
					}
					else
					{
						UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
					}
				}

				if ( pTask->error == eCLE_Success )
				{
					pOrder->itemQuantities[ i ].quantity = newQuantity;
				}
			}
			else
			{
				if ( i < pOrder->itemQuantities.size() )
				{
					if ( i < pOrder->itemQuantities.size() - 1 )
					{
						pOrder->itemQuantities[ i ] = pOrder->itemQuantities[ pOrder->itemQuantities.size() - 1 ];
					}

					pOrder->itemQuantities.resize( pOrder->itemQuantities.size() - 1 );
				}
			}
		}
		else
		{
			UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
		}
	}

	if ( pTask->error == eCLE_Success )
	{
		if ( pTask->subTask == eGST_RevertItemQuantity )
		{
			UpdateTaskError( dlcsTaskID, ( ECryLobbyError )pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] );
		}
		else
		{
			if ( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] == TMemInvalidHdl )
			{
				pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] = CreateTaskParamMem( pTask, GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM, NULL, sizeof ( CryDLCPaymentMethodID ) );

				if ( pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] == eCLE_Success )
				{
					new ( static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) ) ) CryDLCPaymentMethodID( pOrder->paymentMethodID );
					StartSubTask( eGST_GetOrderTotal, dlcsTaskID );
				}
			}
			else
			{
				if ( pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] == eCLE_Success )
				{
					StopTaskRunning( dlcsTaskID );
				}
				else
				{
					StartSubTask( eGST_RevertItemQuantity, dlcsTaskID );
				}
			}
		}
	}
}


ECryLobbyError CCryGameSpyDLCStore::OrderListItems( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCOrderListItemsCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderListItems, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderListItems error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderListItems( CryDLCStoreTaskID dlcsTaskID )
{
	StopTaskRunning( dlcsTaskID );
}


void CCryGameSpyDLCStore::EndOrderListItems( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];
	int32							last = pOrder->itemQuantities.size() - 1;
	int32							i;

	if ( pTask->error == eCLE_Success )
	{
		for ( i = 0; i < last; ++i )
		{
			( ( CryDLCOrderListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_SuccessContinue, pOrder->itemQuantities[ i ].itemID, pOrder->itemQuantities[ i ].quantity, pTask->pCbArg );
		}

		if ( i == last )
		{
			( ( CryDLCOrderListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, pOrder->itemQuantities[ last ].itemID, pOrder->itemQuantities[ i ].quantity, pTask->pCbArg );
		}
		else
		{
			( ( CryDLCOrderListItemsCallback )pTask->pCb )( pTask->lTaskID, eCLE_Success, NULL, 0, pTask->pCbArg );
		}
	}
	else
	{
		( ( CryDLCOrderListItemsCallback )pTask->pCb )( pTask->lTaskID, pTask->error, NULL, 0, pTask->pCbArg );
	}
}


ECryLobbyError CCryGameSpyDLCStore::OrderGetInfo( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCOrderGetInfoCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderGetInfo, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderGetInfo error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	StopTaskRunning( dlcsTaskID );
}


void CCryGameSpyDLCStore::EndOrderGetInfo( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];
	SCryDLCOrderInfo	info;
	wchar_t*					pCurrency = L"";
	wchar_t*					pCulture = L"";
	wchar_t*					pSubtotal = L"";
	wchar_t*					pTax = L"";
	wchar_t*					pTotal = L"";

	if ( pOrder->pTotal )
	{
		if ( pOrder->pTotal->mOrder.mGeoInfo.mCurrencyCode )
		{
			pCurrency = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrder.mGeoInfo.mCurrencyCode );
		}

		if ( pOrder->pTotal->mOrder.mGeoInfo.mCultureCode )
		{
			pCulture = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrder.mGeoInfo.mCultureCode );
		}

		if ( pOrder->pTotal->mOrder.mSubTotal )
		{
			pSubtotal = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrder.mSubTotal );
		}

		if ( pOrder->pTotal->mOrder.mTax )
		{
			pTax = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrder.mTax );
		}

		if ( pOrder->pTotal->mOrder.mTotal )
		{
			pTotal = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrder.mTotal );
		}
	}

	info.currency = pCurrency;
	info.culture = pCulture;
	FixCurrencyString( pSubtotal, info.subtotal );
	FixCurrencyString( pTax, info.tax );
	FixCurrencyString( pTotal, info.total );

	( ( CryDLCOrderGetInfoCallback )pTask->pCb )( pTask->lTaskID, pTask->error, &info, pTask->pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::OrderGetItemInfo( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCOrderGetItemInfoCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	if ( itemID )
	{
		error = StartTaskWithExistingOrder( eT_OrderGetItemInfo, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

		if ( error == eCLE_Success )
		{
			SCryGameSpyDLCItemID*	pID = static_cast< SCryGameSpyDLCItemID* >( itemID.get() );
			SItemGetInfoParam			param;

			param.itemID = pID->itemID;
			param.index = -1;

			error = CreateTaskParamMem( dlcsTaskID, ORDER_GET_ITEM_INFO_PARAM, &param, sizeof ( SItemGetInfoParam ) );

			if ( error == eCLE_Success )
			{
				STask*						pTask = &m_tasks[ dlcsTaskID ];

				InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
				FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
			}

			if ( error != eCLE_Success )
			{
				FreeTask( dlcsTaskID );
			}
		}
	}
	else
	{
		error = eCLE_InvalidParam;
	}

	CryLogAlways( "[Lobby] Start OrderGetInfo error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( !m_catalogReady )
	{
		StartSubTask( eGST_RefreshCatalog, dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID )
{
	if ( StartTaskUsingCatalog( dlcsTaskID ) )
	{
		STask*							pTask = &m_tasks[ dlcsTaskID ];
		SItemGetInfoParam*	pParam = static_cast< SItemGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ ORDER_GET_ITEM_INFO_PARAM ] ) );

		for ( int32 i = 0; ( i < m_catalogItems.size() ) && ( pParam->index == -1 ); ++i )
		{
			if ( static_cast< SCryGameSpyDLCItemID* >( m_catalogItems[ i ].id.get() )->itemID == pParam->itemID )
			{
				pParam->index = i;
			}
		}

		if ( pParam->index == -1 )
		{
			UpdateTaskError( dlcsTaskID, eCLE_InternalError );
		}

		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::EndOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID )
{
	STask*								pTask = &m_tasks[ dlcsTaskID ];
	SOrder*								pOrder = &m_orders[ pTask->order ];
	SCryDLCItemInfo*			pItemInfo = NULL;
	SCryDLCOrderItemInfo*	pOrderItemInfo = NULL;
	SCryDLCOrderItemInfo	orderItemInfo;

	if ( pTask->error == eCLE_Success )
	{
		SItemGetInfoParam*		pParam = static_cast< SItemGetInfoParam* >( m_pLobby->MemGetPtr( pTask->paramsMem[ ORDER_GET_ITEM_INFO_PARAM ] ) );
		
		pItemInfo = &m_catalogItems[ pParam->index ];
		pOrderItemInfo = &orderItemInfo;

		wchar_t*	pTax = L"";
		wchar_t*	pSubtotal = L"";
		wchar_t*	pTotal = L"";

		if ( pOrder->pTotal )
		{
			for ( uint32 i = 0; i < pOrder->pTotal->mOrderItemList.mCount; ++i )
			{
				if ( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItem.mItemId == pParam->itemID )
				{
					if ( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItem.mTax )
					{
						pTax = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItem.mTax );
					}

					if ( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItemTotal.mSubTotal )
					{
						pSubtotal = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItemTotal.mSubTotal );
					}

					if ( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItemTotal.mTotal )
					{
						pTotal = reinterpret_cast< wchar_t* >( pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItemTotal.mTotal );
					}

					orderItemInfo.quantity = pOrder->pTotal->mOrderItemList.mOrderItems[ i ].mItemTotal.mQuantity;
					break;
				}
			}
		}
		else
		{
			for ( uint32 i = 0; i < pOrder->itemQuantities.size(); ++i )
			{
				if ( static_cast< SCryGameSpyDLCItemID* >( pOrder->itemQuantities[ i ].itemID.get() )->itemID == pParam->itemID )
				{
					orderItemInfo.quantity = pOrder->itemQuantities[ i ].quantity;
					break;
				}
			}
		}

		FixCurrencyString( pTax, orderItemInfo.tax );
		FixCurrencyString( pSubtotal, orderItemInfo.subtotal );
		FixCurrencyString( pTotal, orderItemInfo.total );
	}

	( ( CryDLCOrderGetItemInfoCallback )pTask->pCb )( pTask->lTaskID, pTask->error, pItemInfo, pOrderItemInfo, pTask->pCbArg );
}


ECryLobbyError CCryGameSpyDLCStore::OrderSetPaymentMethod( CryDLCOrderHandle orderHandle, CryDLCPaymentMethodID paymentMethodID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderSetPaymentMethod, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		STask*						pTask = &m_tasks[ dlcsTaskID ];
		SOrder*						pOrder = &m_orders[ orderHandle ];

		error = CreateTaskParamMem( pTask, GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM, NULL, sizeof ( CryDLCPaymentMethodID ) );

		if ( error == eCLE_Success )
		{
			new ( static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) ) ) CryDLCPaymentMethodID( paymentMethodID );
			pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] = eCLE_Success;
			InitializeTaskGameSpyObjects( pTask, eGSOB_AuthService, 0 );
			FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
		}
	}

	CryLogAlways( "[Lobby] Start OrderListItems error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderSetPaymentMethod( CryDLCStoreTaskID dlcsTaskID )
{
	STask*													pTask = &m_tasks[ dlcsTaskID ];
	SOrder*													pOrder = &m_orders[ pTask->order ];
	CryDLCPaymentMethodID&					paymentMethodID = *static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) );

	if ( paymentMethodID != pOrder->paymentMethodID )
	{
		StartSubTask( eGST_GetOrderTotal, dlcsTaskID );
	}
	else
	{
		StopTaskRunning( dlcsTaskID );
	}
}


void CCryGameSpyDLCStore::TickOrderSetPaymentMethod( CryDLCStoreTaskID dlcsTaskID )
{
	STask*													pTask = &m_tasks[ dlcsTaskID ];
	SOrder*													pOrder = &m_orders[ pTask->order ];
	CryDLCPaymentMethodID&					paymentMethodID = *static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) );

	if ( pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] == eCLE_Success )
	{
		pOrder->paymentMethodID = paymentMethodID;
	}
	else
	{
		UpdateTaskError( pTask, ( ECryLobbyError )pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] );
	}

	StopTaskRunning( dlcsTaskID );
}


ECryLobbyError CCryGameSpyDLCStore::OrderCancel( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderCancel, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderCancel error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderCancel( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];

	if ( pOrder->transactionState == eTS_NotStarted )
	{
		FreeOrderHandle( pTask->order );
		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( pTask, eCLE_InvalidRequest );
	}
}


ECryLobbyError CCryGameSpyDLCStore::OrderFree( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError		error;
	CryDLCStoreTaskID	dlcsTaskID;

	error = StartTaskWithExistingOrder( eT_OrderFree, &dlcsTaskID, pTaskID, orderHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		FROM_GAME_TO_LOBBY( &CCryGameSpyDLCStore::StartTaskRunning, this, dlcsTaskID );
	}

	CryLogAlways( "[Lobby] Start OrderFree error %d", error );

	return error;
}


void CCryGameSpyDLCStore::StartOrderFree( CryDLCStoreTaskID dlcsTaskID )
{
	STask*						pTask = &m_tasks[ dlcsTaskID ];
	SOrder*						pOrder = &m_orders[ pTask->order ];

	if ( ( pOrder->transactionState == eTS_Succeeded ) || ( pOrder->transactionState == eTS_Failed ) )
	{
		FreeOrderHandle( pTask->order );
		StopTaskRunning( dlcsTaskID );
	}
	else
	{
		UpdateTaskError( pTask, eCLE_InvalidRequest );
	}
}


void CCryGameSpyDLCStore::TickRefreshCatalog( CryDLCStoreTaskID dlcsTaskID )
{
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( !pTask->refreshingCatalog )
	{
		if ( StartTaskRefreshingCatalog( dlcsTaskID ) )
		{
			if ( !m_catalogRefreshing )
			{
				if ( GS_SUCCEEDED( d2gLoadCatalogItems( m_pD2GInstance, m_pD2GCatalog, LoadCatalogItemsCallback, this ) ) )
				{
					AddRef();
					m_catalogRefreshing = true;
					m_catalogReady = false;
				}
				else
				{
					UpdateTaskError( dlcsTaskID, eCLE_InternalError );
				}
			}
		}
		else
		{
			UpdateTaskError( dlcsTaskID, eCLE_InvalidRequest );
		}
	}
	else
	{
		if ( !m_catalogRefreshing )
		{
			StopTaskRefreshingCatalog( dlcsTaskID );

			if ( !m_catalogReady )
			{
				UpdateTaskError( dlcsTaskID, eCLE_InternalError );
			}
			else
			{
				StartSubTask( pTask->startedTask, dlcsTaskID );
			}
		}
	}
}


void CCryGameSpyDLCStore::TickGetOrderTotal( CryDLCStoreTaskID dlcsTaskID )
{
	STask*													pTask = &m_tasks[ dlcsTaskID ];
	SOrder*													pOrder = &m_orders[ pTask->order ];
	SCryGameSpyDLCPaymentMethodID*	pPaymentMethodID = static_cast< SCryGameSpyDLCPaymentMethodID* >( static_cast< CryDLCPaymentMethodID* >( m_pLobby->MemGetPtr( pTask->paramsMem[ GET_ORDER_TOTAL_PAYMENT_METHOD_ID_PARAM ] ) )->get() );
	uint32													itemCount = pOrder->itemQuantities.size();
	uint32													nextSubTask = pTask->startedTask;

	if ( itemCount && pPaymentMethodID )
	{
		if ( !pTask->usingCatalog )
		{
			if ( StartTaskUsingCatalog( dlcsTaskID ) )
			{
				D2GItemId*											pItemIDs = new D2GItemId[ itemCount ];
				uint32*													pItemQuantities = new uint32[ itemCount ];

				if ( pItemIDs && pItemQuantities && CreateOrderLink( pTask->order ) )
				{
					for ( uint32 i = 0; i < itemCount; ++i )
					{
						pItemIDs[ i ] = static_cast< SCryGameSpyDLCItemID* >( pOrder->itemQuantities[ i ].itemID.get() )->itemID;
						pItemQuantities[ i ] = pOrder->itemQuantities[ i ].quantity;
					}

					if ( GS_SUCCEEDED( d2gGetOrderTotal( m_pD2GInstance, m_pD2GCatalog, pPaymentMethodID->accountID, pItemIDs, itemCount, pItemQuantities, GetOrderTotalCallback, pOrder->pLink ) ) )
					{
						PassOrderLinkToGameSpy( pTask->order );
						pOrder->totalState = eTS_Updating;
					}
				}
				else
				{
					pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] = eCLE_OutOfMemory;
				}

				SAFE_DELETE_ARRAY( pItemIDs );
				SAFE_DELETE_ARRAY( pItemQuantities );
			}
			else
			{
				pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] = eCLE_InvalidRequest;
			}

			if ( pOrder->totalState != eTS_Updating )
			{
				pOrder->totalState = eTS_UpdateFailed;
			}
		}
		
		if ( pTask->usingCatalog && ( pOrder->totalState != eTS_Updating ) )
		{
			StopTaskUsingCatalog( dlcsTaskID );

			if ( pOrder->totalState == eTS_UpdateFailed )
			{
				if ( pTask->startedTask == eT_OrderAdjustItemQuantity )
				{
					pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ] = -pTask->paramsNum[ ORDER_ADJUST_ITEM_QUANTITY_QUANTITY_PARAM ];
					nextSubTask = eGST_RevertItemQuantity;
				}

				pTask->paramsNum[ GET_ORDER_TOTAL_ERROR_PARAM ] = eCLE_InvalidRequest;
			}

			if ( pOrder->pTotal )
			{
				pOrder->totalState = eTS_Valid;
			}
			else
			{
				pOrder->totalState = eTS_Invalid;
			}
		}
	}
	else
	{
		if ( pOrder->pTotal )
		{
			d2gFreeOrderTotal( pOrder->pTotal );
		}

		pOrder->pTotal = NULL;
		pOrder->totalState = eTS_Invalid;
	}

	if ( pOrder->totalState != eTS_Updating )
	{
		StartSubTask( nextSubTask, dlcsTaskID );
	}
}


bool CCryGameSpyDLCStore::EnsureD2GInitialised( CryDLCStoreTaskID dlcsTaskID )
{
	STask*										pTask = &m_tasks[ dlcsTaskID ];

	if ( !( m_pD2GInstance && m_pD2GCatalog ) )
	{
		CCryGameSpyLobbyService*	pLobbyService = static_cast< CCryGameSpyLobbyService* >( m_pLobbyService );
		GSLoginCertificate*				pCertificate;
		GSLoginPrivateData*				pPrivateData;

		m_pD2GInstance = d2gCreateInstance();

		if ( !m_pD2GInstance )
		{
			UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
		}

		if ( pTask->error == eCLE_Success )
		{
			pCertificate = pLobbyService->AuthServiceGetCertificate();
			pPrivateData = pLobbyService->AuthServiceGetPrivateData();

			if ( !( pCertificate && pPrivateData && GS_SUCCEEDED( d2gInitialize( m_pD2GInstance, pCertificate, pPrivateData ) ) ) )
			{
				UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
			}
		}

		if ( pTask->error == eCLE_Success )
		{
			SConfigurationParams	neededInfo[ 4 ] =
			{
				{ CLCC_GAMESPY_GAMEID,						{ NULL } },
				{ CLCC_GAMESPY_D2GCATALOGVERSION,	{ NULL } },
				{ CLCC_GAMESPY_D2GCATALOGREGION,	{ NULL } },
				{ CLCC_GAMESPY_D2GCATALOGTOKEN,		{ NULL } }
			};

			m_pLobby->GetConfigurationInformation( neededInfo, 4 );

			if ( neededInfo[ 2 ].m_pData )
			{
				CryStringT< wchar_t >	region;

				region.Format( L"%S", static_cast< const char* >( neededInfo[ 2 ].m_pData ) );

				if ( neededInfo[ 3 ].m_pData )
				{
					CryStringT< wchar_t >	token;

					token.Format( L"%S", static_cast< const char* >( neededInfo[ 3 ].m_pData ) );
					m_pD2GCatalog = d2gCreateCatalog( m_pD2GInstance, neededInfo[ 0 ].m_32, neededInfo[ 1 ].m_32, reinterpret_cast< UCS2String >( const_cast< wchar_t* >( region.c_str() ) ), reinterpret_cast< UCS2String >( const_cast< wchar_t* >( token.c_str() ) ) );
				}
			}

			if ( !m_pD2GCatalog )
			{
				UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
			}
		}
	}

	return pTask->error == eCLE_Success;
}


void CCryGameSpyDLCStore::GetStoreAvailabilityCallback( GSResult result, D2GGetStoreAvailabilityResponse* pResponse, void* pUserData )
{
	LOBBY_AUTO_LOCK;

	CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pUserData );

	if ( ( pResponse->mHttpResult == GHTTPSuccess ) && GS_SUCCEEDED( result ) )
	{
		switch ( GS_RESULT_CODE( pResponse->mAvailabilityCode ) )
		{
		case D2GResultCode_StoreOnline:
			pThis->m_storeStatus = eCDLCSS_Online;
			break;
		case D2GResultCode_StoreOfflineForMaintenance: 
			pThis->m_storeStatus = eCDLCSS_OfflineForMaintanance;
			break;
		case D2GResultCode_StoreOfflineRetired:       
			pThis->m_storeStatus = eCDLCSS_Discontinued;
			break;
		case D2GResultCode_StoreNotYetLaunched:
			pThis->m_storeStatus = eCDLCSS_NotYetLaunched;
			break;
		default:
			// Not sure if this can happen.
			pThis->m_storeStatus = eCDLCSS_QueryFailed;
			break;
		}
	}
	else
	{
		pThis->m_storeStatus = eCDLCSS_QueryFailed;
	}

	pThis->Release();
}


void CCryGameSpyDLCStore::LoadCatalogItemsCallback( GSResult result, D2GLoadCatalogItemsResponse *pResponse, void *pUserData )
{
	LOBBY_AUTO_LOCK;

	CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pUserData );

	if ( ( pResponse->mHttpResult == GHTTPSuccess ) && GS_SUCCEEDED( result  ) )
	{
		if ( pThis->m_pD2GCatalogItemList )
		{
			d2gFreeCatalogItemList( pThis->m_pD2GCatalogItemList );
		}

		if ( pThis->m_D2GCategoryList.mCategoryNames )
		{
			gsifree( pThis->m_D2GCategoryList.mCategoryNames );
			pThis->m_D2GCategoryList.mCategoryNames = NULL;
			pThis->m_D2GCategoryList.mCount = 0;
		}

		pThis->m_pD2GCatalogItemList = pResponse->mItemList;

		GSResult categoriesResult = d2gGetCategories( pThis->m_pD2GInstance, pThis->m_pD2GCatalog, &pThis->m_D2GCategoryList );

		if ( GS_SUCCEEDED( categoriesResult ) || GS_RESULT_CODE(categoriesResult) == D2GResultCode_Catalog_Empty )
		{
			pThis->m_catalogCategories.clear();

			SCryDLCCategoryInfo				categoryInfo;
			SCryGameSpyDLCCategoryID*	pCategoryID;
			uint32										i;
			uint32										j;
			uint32										k;

			for ( i = 0; i < pThis->m_D2GCategoryList.mCount; ++i )
			{
				pCategoryID = new SCryGameSpyDLCCategoryID();

				if ( !pCategoryID )
				{
					break;
				}

				pCategoryID->name = reinterpret_cast< wchar_t* >( pThis->m_D2GCategoryList.mCategoryNames[ i ] );
				categoryInfo.id = pCategoryID;
				categoryInfo.name = pCategoryID->name;
				pThis->m_catalogCategories.push_back( categoryInfo );
			}

			if ( i == pThis->m_D2GCategoryList.mCount )
			{
				for ( i = 0; i < pResponse->mItemList->mCount; ++i )
				{
					SCryDLCItemInfo						itemInfo;
					SCryGameSpyDLCItemID*			pItemID;

					pItemID = new SCryGameSpyDLCItemID();

					if ( !pItemID )
					{
						break;
					}

					wchar_t*	pName = L"";
					wchar_t*	pDescription = L"";
					wchar_t*	pCurrency = L"";
					wchar_t*	pCulture = L"";
					wchar_t*	pPrice = L"";

					pItemID->itemID = pResponse->mItemList->mCatalogItems[ i ].mItem.mItemId;
					itemInfo.id = pItemID;

					if ( pResponse->mItemList->mCatalogItems[ i ].mItem.mName )
					{
						pName = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mItem.mName );
					}

					if ( pResponse->mItemList->mCatalogItems[ i ].mProductInfo.mSummary )
					{
						pDescription = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mProductInfo.mSummary );
					}

					if ( pResponse->mItemList->mCatalogItems[ i ].mGeoInfo.mCurrencyCode )
					{
						pCurrency = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mGeoInfo.mCurrencyCode );
					}

					if ( pResponse->mItemList->mCatalogItems[ i ].mGeoInfo.mCultureCode )
					{
						pCulture = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mGeoInfo.mCultureCode );
					}

					if ( pResponse->mItemList->mCatalogItems[ i ].mItem.mPrice )
					{
						pPrice = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mItem.mPrice );
					}

					itemInfo.name = pName;
					itemInfo.description = pDescription;
					itemInfo.currency = pCurrency;
					itemInfo.culture = pCulture;
					pThis->FixCurrencyString( pPrice, itemInfo.price );
					itemInfo.allowQuantity = false;

					for ( j = 0; j < pResponse->mItemList->mCatalogItems[ i ].mCategories.mCount; ++j )
					{
						for ( k = 0; k < pThis->m_catalogCategories.size(); ++k )
						{
							if ( !wcscmp( pThis->m_catalogCategories[ k ].name.c_str(), reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mCategories.mCategoryNames[ j ] ) ) )
							{
								itemInfo.categories.push_back( pThis->m_catalogCategories[ k ].id );
							}
						}
					}

					SCryGameSpyDLCImageID*	pImageID;

					for ( j = 0; j < pResponse->mItemList->mCatalogItems[ i ].mImages.mCount; ++j )
					{
						pImageID = new SCryGameSpyDLCImageID();

						if ( !pImageID )
						{
							break;
						}

						pImageID->name = reinterpret_cast< wchar_t* >( pResponse->mItemList->mCatalogItems[ i ].mImages.mImageName[ j ] );
						itemInfo.images.push_back( pImageID );
					}

					if ( j < pResponse->mItemList->mCatalogItems[ i ].mImages.mCount )
					{
						break;
					}

					pThis->m_catalogItems.push_back( itemInfo );
				}

				if ( i == pResponse->mItemList->mCount )
				{
					pThis->m_catalogReady = true;
				}
			}
		}
	}

	pThis->m_catalogRefreshing = false;

	pThis->Release();
}


void CCryGameSpyDLCStore::GetPurchaseHistoryCallback( GSResult result, D2GGetPurchaseHistoryResponse *pResponse, void *pUserData )
{
	LOBBY_AUTO_LOCK;

	STaskLink*						pLink = static_cast< STaskLink* >( pUserData );

	if ( pLink )
	{
		STask*								pTask = pLink->m_pTask;

		if ( pTask )
		{
			CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pLink->m_pDLCStore.get() );
			ECryLobbyError				error;

			if ( ( pResponse->mHttpResult == GHTTPSuccess ) && GS_SUCCEEDED( result ) && ( pResponse->mPurchaseHistory ) )
			{
				pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = CountItemsInPurchaseHistory( pResponse->mPurchaseHistory );				
				error = pThis->CreateTaskParamMem( pTask, COMMON_PURCHASE_HISTORY_PARAM, NULL, sizeof ( SPurchaseHistoryItem ) * pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] );

				if ( error == eCLE_Success )
				{
					SPurchaseHistoryItem*	pItem = static_cast< SPurchaseHistoryItem* >( pLink->m_pLobby->MemGetPtr( pTask->paramsMem[ COMMON_PURCHASE_HISTORY_PARAM ] ) );

					pThis->WriteItemsInPurchaseHistory( pTask, pResponse->mPurchaseHistory, pItem );
				}
			}
			else
			{
				pTask->paramsNum[ COMMON_PURCHASE_HISTORY_PARAM ] = GET_PURCHASE_HISTORY_FAILED;
			}
		}

		FreeTaskLinkFromGameSpy( pLink );
	}
}


uint32 CCryGameSpyDLCStore::CountItemsInPurchaseHistory( D2GPurchaseHistory* pPurchaseHistory )
{
	uint32		result = 0;

	for ( uint32 i = 0; i < pPurchaseHistory->mCount; ++i )
	{
		result += CountItemsInOrderPurchase( &pPurchaseHistory->mPurchases[ i ] );
	}

	return result;
}


uint32 CCryGameSpyDLCStore::CountItemsInOrderPurchase( D2GOrderPurchase* pOrderPurchase )
{
	uint32		result = 0;

	for ( uint32 i = 0; i < pOrderPurchase->mItemPurchases.mCount; ++i )
	{
		result += CountItemsInOrderItemPurchase( &pOrderPurchase->mItemPurchases.mOrderItemPurchases[ i ] );
	}

	return result;
}


uint32 CCryGameSpyDLCStore::CountItemsInOrderItemPurchase( D2GOrderItemPurchase* pOrderItemPurchase )
{
	uint32		result = pOrderItemPurchase->mDownloadList.mCount + pOrderItemPurchase->mLicenseList.mCount + 1;

	for ( uint32 i = 0; i < pOrderItemPurchase->mPurchaseList.mCount; ++i )
	{
		result += CountItemsInOrderItemPurchase( &pOrderItemPurchase->mPurchaseList.mOrderItemPurchases[ i ] );
	}

	return result;
}


void CCryGameSpyDLCStore::WriteItemsInPurchaseHistory( STask* pTask, D2GPurchaseHistory* pPurchaseHistory, SPurchaseHistoryItem*& pItem )
{
	for ( uint32 i = 0; i < pPurchaseHistory->mCount; ++i )
	{
		WriteItemsInOrderPurchase( pTask, &pPurchaseHistory->mPurchases[ i ], pItem );
	}
}


void CCryGameSpyDLCStore::WriteItemsInOrderPurchase( STask* pTask, D2GOrderPurchase* pOrderPurchase, SPurchaseHistoryItem*& pItem )
{
	for ( uint32 i = 0; i < pOrderPurchase->mItemPurchases.mCount; ++i )
	{
		WriteItemsInOrderItemPurchase( pTask, &pOrderPurchase->mItemPurchases.mOrderItemPurchases[ i ], pItem );
	}
}


void CCryGameSpyDLCStore::WriteItemsInOrderItemPurchase( STask* pTask, D2GOrderItemPurchase* pOrderItemPurchase, SPurchaseHistoryItem*& pItem )
{
	pItem->type = ePHIT_Purchasable;
	pItem->data.purchasable.itemID = pOrderItemPurchase->mOrderItem.mItem.mItemId;
	++pItem;

	for ( uint32 i = 0; i < pOrderItemPurchase->mDownloadList.mCount; ++i )
	{
		pItem->type = ePHIT_Downloadable;
		pItem->data.downloadable.fileID = pOrderItemPurchase->mDownloadList.mDownloads[ i ].mFileId;

		SCryDLCDownloadableInfo	info;
		SCryGameSpyDLCDownloadableID*	pID = new SCryGameSpyDLCDownloadableID();

		if ( pID )
		{
			pID->fileID = pOrderItemPurchase->mDownloadList.mDownloads[ i ].mFileId;
			info.id = pID;
			info.name = reinterpret_cast< wchar_t* >( pOrderItemPurchase->mDownloadList.mDownloads[ i ].mName );
			info.assetType = reinterpret_cast< wchar_t* >( pOrderItemPurchase->mDownloadList.mDownloads[ i ].mAssetType );
			info.sequence = pOrderItemPurchase->mDownloadList.mDownloads[ i ].mSequence;
			info.version = pOrderItemPurchase->mDownloadList.mDownloads[ i ].mVersion;
			UpdateDownloadable( info );
		}

		++pItem;
	}

	for ( uint32 i = 0; i < pOrderItemPurchase->mLicenseList.mCount; ++i )
	{
		wchar_t*	pName = reinterpret_cast< wchar_t* >( pOrderItemPurchase->mLicenseList.mLicenses[ i ].mLicenseName );
		wchar_t*	pKey = reinterpret_cast< wchar_t* >( pOrderItemPurchase->mLicenseList.mLicenses[ i ].mLicenseKey );

		pItem->type = ePHIT_License;
		pItem->data.license.name = new wchar_t[ wcslen( pName ) + 1 ];
		pItem->data.license.key = new wchar_t[ wcslen( pKey ) + 1 ];

		if ( ( pItem->data.license.name && pItem->data.license.key ) )
		{
			wcscpy( pItem->data.license.name, pName );
			wcscpy( pItem->data.license.key, pKey );
		}
		else
		{
			UpdateTaskError( pTask, eCLE_OutOfMemory );
		}

		++pItem;
	}

	for ( uint32 i = 0; i < pOrderItemPurchase->mPurchaseList.mCount; ++i )
	{
		WriteItemsInOrderItemPurchase( pTask, &pOrderItemPurchase->mPurchaseList.mOrderItemPurchases[ i ], pItem );
	}
}


void CCryGameSpyDLCStore::GetUserCreditCardsCallback( GSResult result, D2GGetUserCreditCardsResponse *pResponse, void *pUserData )
{
	LOBBY_AUTO_LOCK;

	STaskLink*						pLink = static_cast< STaskLink* >( pUserData );

	if ( pLink )
	{
		STask*								pTask = pLink->m_pTask;

		if ( pTask )
		{
			ECryLobbyError				error = eCLE_InternalError;
			CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pLink->m_pDLCStore.get() );

			if ( pThis )
			{
				if ( pResponse->mHttpResult == GHTTPSuccess ) 
				{
					if ( GS_SUCCEEDED( result ) )
					{
						error = pThis->CreateTaskParamMem( pTask, USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM, NULL, sizeof ( SCryDLCPaymentMethodInfo ) * pResponse->mListOfCreditCards->mCount );

						if ( error == eCLE_Success )
						{
							SCryDLCPaymentMethodInfo*	pInfo = static_cast< SCryDLCPaymentMethodInfo* >( pLink->m_pLobby->MemGetPtr( pTask->paramsMem[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ] ) );
							
							pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ] = pResponse->mListOfCreditCards->mCount;

							for ( uint32 i = 0; i < pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ]; ++i )
							{
								new ( &pInfo[ i ] ) SCryDLCPaymentMethodInfo();
							}

							for ( uint32 i = 0; ( i < pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_PAYMENT_METHODS_PARAM ] ) && ( error == eCLE_Success ); ++i )
							{
								SCryGameSpyDLCPaymentMethodID*	pID = new SCryGameSpyDLCPaymentMethodID();

								if ( pID )
								{
									pID->accountID = pResponse->mListOfCreditCards->mCreditCardInfos[ i ].mAccountId;
									pInfo[ i ].id = pID;
									pInfo[ i ].type = reinterpret_cast< wchar_t* >( pResponse->mListOfCreditCards->mCreditCardInfos[ i ].mCreditCardType );
									pInfo[ i ].expirationDate = pResponse->mListOfCreditCards->mCreditCardInfos[ i ].mExpirationDate;
									pInfo[ i ].isDefault = pResponse->mListOfCreditCards->mCreditCardInfos[ i ].mIsDefault == gsi_true;
									pInfo[ i ].lastFourDigits = pResponse->mListOfCreditCards->mCreditCardInfos[ i ].mLastFourDigits;
								}
								else
								{
									error = eCLE_OutOfMemory;
								}
							}
						}
					}
					else
					{
						GSResult	code = GS_RESULT_CODE( result );
						GSResult	section = GS_RESULT_SECTION( result ) >> GS_RESULT_CODEBITS;
						GSResult	sdk = GS_RESULT_SDK( result ) >> ( GS_RESULT_CODEBITS + GS_RESULT_SECTIONBITS );

						if ( sdk == GSResultSDK_Direct2Game )
						{
							if ( section == GSResultSection_Account )
							{
								if ( code == GSResultCode_InvalidCertificate )
								{
									static_cast< CCryGameSpyLobbyService* >( pThis->m_pLobbyService )->AuthServiceInvalidateCertificate();
								}
							}
						}

						error = eCLE_InternalError;
					}
				}
			}

			pTask->paramsNum[ USER_LIST_PAYMENT_METHODS_STATE_PARAM ] = USER_LIST_PAYMENT_METHODS_STATE_COMPLETE;
			UpdateTaskError( pTask, error );
		}

		FreeTaskLinkFromGameSpy( pLink );
	}

	if ( pResponse->mListOfCreditCards )
	{
		d2gFreeCreditCardInfoList( pResponse->mListOfCreditCards );
	}
}


void CCryGameSpyDLCStore::GetOrderTotalCallback( GSResult result, D2GGetOrderTotalResponse *pResponse, void *pUserData )
{
	LOBBY_AUTO_LOCK;

	SOrderLink*						pLink = static_cast< SOrderLink* >( pUserData );

	if ( pLink )
	{
		SOrder*								pOrder = pLink->m_pOrder;

		if ( pOrder )
		{
			CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pLink->m_pDLCStore.get() );

			if ( pThis )
			{
				D2GOrderTotal*				pTotal = NULL;

				if ( pResponse->mHttpResult == GHTTPSuccess ) 
				{
					if ( GS_SUCCEEDED( result ) )
					{
						pTotal = pResponse->mOrderTotal;
					}
					else
					{
						if ( result == GS_RESULT( 1, GSResultSDK_Direct2Game, GSResultSection_Account, GSResultCode_InvalidCertificate ) )
						{
							static_cast< CCryGameSpyLobbyService* >( pThis->m_pLobbyService )->AuthServiceInvalidateCertificate();
						}
					}
				}

				if ( pTotal )
				{
					if ( pOrder->pTotal )
					{
						d2gFreeOrderTotal( pOrder->pTotal );
					}

					pOrder->pTotal = pTotal;
					pOrder->totalState = eTS_Valid;
				}
				else
				{
					pOrder->totalState = eTS_UpdateFailed;
				}
			}
		}

		FreeOrderLinkFromGameSpy( pLink );
	}
}


void CCryGameSpyDLCStore::StartOrderCallback( GSResult result, D2GStartOrderResponse* pResponse, void* pUserData )
{
	LOBBY_AUTO_LOCK;

	SOrderLink*						pLink = static_cast< SOrderLink* >( pUserData );
	D2GOrderPurchase*			pOrderPurchaseToFree = pResponse->mOrderPurchase;

	if ( pLink )
	{
		SOrder*								pOrder = pLink->m_pOrder;

		if ( pOrder )
		{
			CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pLink->m_pDLCStore.get() );

			if ( pThis )
			{
				if ( pResponse->mHttpResult == GHTTPSuccess ) 
				{
					if ( GS_SUCCEEDED( result ) )
					{
						if ( pResponse->mOrderPurchase )
						{
							if ( pOrder->used )
							{
								pOrderPurchaseToFree = pOrder->pPurchase;
								pOrder->pPurchase = pResponse->mOrderPurchase;

								switch ( GS_RESULT_CODE( pOrder->pPurchase->mOrder.mValidation.mResult ) )
								{
								case D2GResultCode_Order_Ok:
									pOrder->transactionState = eTS_Succeeded;
									break;
								case D2GResultCode_Order_BillingPending:
									pOrder->transactionState = eTS_Started;
									break;
								default:
									pOrder->transactionState = eTS_Failed;
									break;
								}

								pOrder->lastPollTime = g_time + CTimeValue( ORDER_POLL_DELAY - ORDER_POLL_INTERVAL );
								pOrder->polling = false;
							}
						}
					}
					else
					{
						if ( result == GS_RESULT( 1, GSResultSDK_Direct2Game, GSResultSection_Account, GSResultCode_InvalidCertificate ) )
						{
							static_cast< CCryGameSpyLobbyService* >( pThis->m_pLobbyService )->AuthServiceInvalidateCertificate();
							pOrder->transactionState = eTS_Failed;
						}
						else
						{
							pOrder->transactionState = eTS_Failed;
						}
					}
				}
			}
		}

		FreeOrderLinkFromGameSpy( pLink );
	}

	if ( pOrderPurchaseToFree )
	{
		d2gFreeOrderPurchase( pOrderPurchaseToFree );
	}
}


void CCryGameSpyDLCStore::OrderCompleteCallback( GSResult result, D2GIsOrderCompleteResponse *pResponse, void *pUserData )
{
	LOBBY_AUTO_LOCK;

	SOrderLink*						pLink = static_cast< SOrderLink* >( pUserData );
	D2GOrderPurchase*			pOrderPurchaseToFree = pResponse->mOrderPurchase;

	if ( pLink )
	{
		SOrder*								pOrder = pLink->m_pOrder;

		if ( pOrder )
		{
			CCryGameSpyDLCStore*	pThis = static_cast< CCryGameSpyDLCStore* >( pLink->m_pDLCStore.get() );

			if ( pThis )
			{
				if ( ( pResponse->mHttpResult == GHTTPSuccess ) && GS_SUCCEEDED( result ) && pResponse->mOrderPurchase )
				{
					if ( pOrder->used && pOrder->polling )
					{
						pOrderPurchaseToFree = pOrder->pPurchase;
						pOrder->pPurchase = pResponse->mOrderPurchase;

						switch ( GS_RESULT_CODE( pOrder->pPurchase->mOrder.mValidation.mResult ) )
						{
						case D2GResultCode_Order_Ok:
							pOrder->transactionState = eTS_Succeeded;
							break;
						case D2GResultCode_Order_BillingPending:
							break;
						default:
							pOrder->transactionState = eTS_Failed;
							break;
						}

						pOrder->lastPollTime = g_time;
						pOrder->polling = false;
					}
				}
			}
		}

		FreeOrderLinkFromGameSpy( pLink );
	}

	if ( pOrderPurchaseToFree )
	{
		d2gFreeOrderPurchase( pOrderPurchaseToFree );
	}
}


void CCryGameSpyDLCStore::DispatchDownloadProgress( CryDLCUserDownloadItemCallback cb, CryLobbyTaskID lTaskID, CryDLCDownloadableID downloadableID, uint32 bytesReceived, uint32 bytesTotal, void* pCbArg )
{
	cb( lTaskID, eCLE_SuccessContinue, downloadableID, false, bytesReceived, bytesTotal, "", pCbArg );
}


void CCryGameSpyDLCStore::DownloadProgressCallbackHandler( STaskLink* pLink, gsi_u32 bytesReceived, gsi_u32 bytesTotal )
{
	if ( pLink )
	{
		STask*			pTask = pLink->m_pTask;

		if ( pTask )
		{
			if ( pTask->pCb )
			{
				SCryGameSpyDLCDownloadableID*	pID = new SCryGameSpyDLCDownloadableID( pTask->paramsNum[ USER_DOWNLOAD_ITEM_ID_PARAM ] );

				if ( pID )
				{
					TO_GAME_FROM_LOBBY( &CCryGameSpyDLCStore::DispatchDownloadProgress, pLink->m_pDLCStore.get(), ( CryDLCUserDownloadItemCallback )pTask->pCb, pTask->lTaskID, CryDLCDownloadableID( pID ), bytesReceived, bytesTotal, pTask->pCbArg );
				}
			}
		}
	}
}


void CCryGameSpyDLCStore::DownloadProgressCallback( gsi_u32 bytesReceived, gsi_u32 bytesTotal, void* pUserData )
{
	// Runs on thread created by GameSpy SDK

	STaskLink*						pLink = static_cast< STaskLink* >( pUserData );

	if ( pLink )
	{
		CCryGameSpyDLCStore*	pDLCStore = pLink->m_pDLCStore.get();

		if ( pDLCStore )
		{
			pDLCStore->m_fromD2GDownloadThreadQueueMutex.Lock();
			pDLCStore->m_fromD2GDownloadThreadQueue.Add( &CCryGameSpyDLCStore::DownloadProgressCallbackHandler, pDLCStore, pLink, bytesReceived, bytesTotal );
			pDLCStore->m_fromD2GDownloadThreadQueueMutex.Unlock();
		}
	}
}


void CCryGameSpyDLCStore::DownloadCompleteCallbackHandler( STaskLink* pLink, GSResult result, string path )
{
	if ( pLink )
	{
		STask*			pTask = pLink->m_pTask;

		if ( pTask )
		{
			if ( GS_SUCCEEDED( result ) )
			{
				uint32			pathSize = strlen( path.c_str() ) + 1;

				if ( pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] != TMemInvalidHdl )
				{
					if ( pathSize > pTask->paramsNum[ USER_DOWNLOAD_ITEM_PATH_PARAM ] )
					{
						m_pLobby->MemFree( pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] );
						pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] = TMemInvalidHdl;
						pTask->paramsNum[ USER_DOWNLOAD_ITEM_PATH_PARAM ] = 0;
					}
				}

				if ( pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] == TMemInvalidHdl )
				{
					ECryLobbyError	error = CreateTaskParamMem( pTask, USER_DOWNLOAD_ITEM_PATH_PARAM, path.c_str(), pathSize );

					if ( error == eCLE_Success )
					{
						pTask->paramsNum[ USER_DOWNLOAD_ITEM_PATH_PARAM ] = pathSize;
						pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_SUCCEEDED;
					}
					else
					{
						pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_FAILED;
						UpdateTaskError( pTask, error );
					}
				}
				else
				{
					strcpy( static_cast< char* >( m_pLobby->MemGetPtr( pTask->paramsMem[ USER_DOWNLOAD_ITEM_PATH_PARAM ] ) ), path.c_str() );
					pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_SUCCEEDED;
				}
			}
			else
			{
				pTask->paramsNum[ USER_DOWNLOAD_ITEM_STATE_PARAM ] = USER_DOWNLOAD_ITEM_FAILED;
				UpdateTaskError( pTask, eCLE_InternalError );
			}
		}

		FreeTaskLinkFromGameSpy( pLink );
	}
}


void CCryGameSpyDLCStore::DownloadCompleteCallback( GSResult result, gsi_char* pSaveFile, gsi_char* url, void* pUserData )
{
	// Runs on thread created by GameSpy SDK

	STaskLink*						pLink = static_cast< STaskLink* >( pUserData );

	if ( pLink )
	{
		CCryGameSpyDLCStore*	pDLCStore = pLink->m_pDLCStore.get();

		if ( pDLCStore )
		{
			pDLCStore->m_fromD2GDownloadThreadQueueMutex.Lock();
			pDLCStore->m_fromD2GDownloadThreadQueue.Add( &CCryGameSpyDLCStore::DownloadCompleteCallbackHandler, pDLCStore, pLink, result, string( pSaveFile ) );
			pDLCStore->m_fromD2GDownloadThreadQueueMutex.Unlock();
		}
	}
}


bool CCryGameSpyDLCStore::CreateTaskLink( CryDLCStoreTaskID dlcsTaskID )
{
	bool		result = false;
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( !pTask->pLink )
	{
		pTask->pLink = new STaskLink( pTask, this, m_pLobby );

		if ( !pTask->pLink )
		{
			UpdateTaskError( dlcsTaskID, eCLE_OutOfMemory );
		}
	}

	if ( pTask->pLink )
	{
		result = true;
	}

	return result;
}


void CCryGameSpyDLCStore::PassTaskLinkToGameSpy( CryDLCStoreTaskID dlcsTaskID )
{
	STask*	pTask = &m_tasks[ dlcsTaskID ];

	if ( pTask->pLink )
	{
		pTask->pLink->AddToVector( m_taskLinks );
	}
}


void CCryGameSpyDLCStore::FreeTaskLinkFromCryNetwork( STask* pTask )
{
	if ( pTask && pTask->pLink )
	{
		pTask->pLink->m_pTask = NULL;
		pTask->pLink->m_pLobby = NULL;
		pTask->pLink = NULL;
	}
}


void CCryGameSpyDLCStore::FreeTaskLinkFromGameSpy( STaskLink* pLink )
{
	if ( pLink )
	{
		pLink->RemoveFromVector();
	}
}


bool CCryGameSpyDLCStore::CreateOrderLink( CryDLCOrderHandle h )
{
	bool		result = false;
	SOrder*	pOrder = &m_orders[ h ];

	if ( !pOrder->pLink )
	{
		pOrder->pLink = new SOrderLink( pOrder, this );
	}

	if ( pOrder->pLink )
	{
		result = true;
	}

	return result;
}


void CCryGameSpyDLCStore::PassOrderLinkToGameSpy( CryDLCOrderHandle h )
{
	SOrder*	pOrder = &m_orders[ h ];

	if ( pOrder->pLink )
	{
		pOrder->pLink->AddToVector( m_orderLinks );
	}
}


void CCryGameSpyDLCStore::FreeOrderLinkFromCryNetwork( SOrder* pOrder )
{
	if ( pOrder && pOrder->pLink )
	{
		pOrder->pLink->m_pOrder = NULL;
		pOrder->pLink->m_pDLCStore = NULL;
		pOrder->pLink = NULL;
	}
}


void CCryGameSpyDLCStore::FreeOrderLinkFromGameSpy( SOrderLink* pLink )
{
	if ( pLink )
	{
		pLink->RemoveFromVector();
	}
}


void CCryGameSpyDLCStore::OnGameSpyObjectAvailable( EGameSpyObjectBit object )
{
	for ( CryMatchMakingTaskID id = 0; id < MAX_DLCSTORE_TASKS; ++id )
	{
		STask*	pTask = &m_tasks[ id ];

		if ( pTask->used )
		{
			pTask->haveGameSpyObjects |= object;
		}
	}
}


void CCryGameSpyDLCStore::OnGameSpyObjectUnavailable( EGameSpyObjectBit object )
{
	for ( CryMatchMakingTaskID id = 0; id < MAX_DLCSTORE_TASKS; ++id )
	{
		STask*	pTask = &m_tasks[ id ];

		if ( pTask->used )
		{
			pTask->haveGameSpyObjects &= ~object;
		}
	}
}


void CCryGameSpyDLCStore::OnGameSpyObjectCantMakeAvailable( EGameSpyObjectBit object )
{
	for ( CryMatchMakingTaskID id = 0; id < MAX_DLCSTORE_TASKS; ++id )
	{
		STask*	pTask = &m_tasks[ id ];

		if ( pTask->used )
		{
			if ( ( pTask->oldGameSpyObjects | pTask->newGameSpyObjects ) & object )
			{
				UpdateTaskError( id, eCLE_UserNotSignedIn );
			}
		}
	}
}


void CCryGameSpyDLCStore::InitializeTaskGameSpyObjects( STask* pTask, uint8 newObjects, uint8 oldObjects )
{
	CCryGameSpyLobbyService*	pService = static_cast< CCryGameSpyLobbyService* >( m_pLobbyService );

	pTask->newGameSpyObjects = newObjects;
	pTask->oldGameSpyObjects = oldObjects;
	pTask->haveGameSpyObjects = pService->GetAvailableGameSpyObjects();

	if ( pTask->newGameSpyObjects & eGSOB_AvailabilityCheck )
	{
		pService->SetNeedAvailabilityCheck( true );
	}

	if ( pTask->newGameSpyObjects & eGSOB_AuthService )
	{
		pService->SetNeedAuthService( true );
	}
}


ICryGameSpyLobbyComponent::ETaskGameSpyCheckResult CCryGameSpyDLCStore::CheckTaskGameSpyObjects( CryMatchMakingTaskID mmTaskID )
{
	ETaskGameSpyCheckResult result = eTGSCR_Wait;
	STask*									pTask = &m_tasks[ mmTaskID ];

	if ( ( pTask->haveGameSpyObjects & pTask->oldGameSpyObjects ) != pTask->oldGameSpyObjects )
	{
		UpdateTaskError( mmTaskID, eCLE_UserNotSignedIn );
		result = eTGSCR_Fail;
	}
	else
	{
		uint32 newObjects = pTask->newGameSpyObjects & pTask->haveGameSpyObjects;

		pTask->newGameSpyObjects &= ~newObjects;
		pTask->oldGameSpyObjects |= newObjects;

		if ( !pTask->newGameSpyObjects )
		{
			result = eTGSCR_Proceed;
		}
	}

	return result;
}


void CCryGameSpyDLCStore::FreeOrderHandle( CryDLCOrderHandle orderHandle )
{
	SOrder*					pOrder = &m_orders[ orderHandle ];

	FreeOrderLinkFromCryNetwork( pOrder );

	if ( pOrder->pTotal )
	{
		d2gFreeOrderTotal( pOrder->pTotal );
	}

	if ( pOrder->pPurchase )
	{
		d2gFreeOrderPurchase( pOrder->pPurchase );
	}

	CCryDLCStore::FreeOrderHandle( orderHandle );
}


void CCryGameSpyDLCStore::ClearOrderHandle( CryDLCOrderHandle orderHandle )
{
	SOrder*					pOrder = &m_orders[ orderHandle ];

	pOrder->pLink = NULL;
	pOrder->lastPollTime = CTimeValue();
	pOrder->pTotal = NULL;
	pOrder->pPurchase = NULL;
	pOrder->totalState = eTS_Invalid;
	pOrder->polling = false;

	CCryDLCStore::ClearOrderHandle( orderHandle );
}

ECryLobbyError CCryGameSpyDLCStore::GetRealDLCItemID( CryDLCItemID itemID, uint32* idOut )
{
	if( idOut == NULL )
	{
		return eCLE_InvalidParam;
	}

	SCryGameSpyDLCItemID*	pID = static_cast< SCryGameSpyDLCItemID* >( itemID.get() );

	if( pID == NULL )
	{
		return eCLE_InvalidParam;
	}

	*idOut = pID->itemID;

	return eCLE_Success;
}

void CCryGameSpyDLCStore::FixCurrencyString( wchar_t* pRawStr, CryStringT< wchar_t >& fixedString )
{
	size_t			numPlaces;

	if ( pRawStr )
	{
		uint32			rawStrLen = wcslen( pRawStr );
		wchar_t*		pPoint;
		wchar_t*		pPoint0 = wcsrchr( pRawStr, L'.' );
		wchar_t*		pPoint1 = wcsrchr( pRawStr, L',' );
		wchar_t*		pLast = pRawStr + rawStrLen;
		wchar_t*		pFirst = pRawStr;

		if ( pPoint0 && pPoint1 )
		{
			if ( pPoint0 > pPoint1 )
			{
				pPoint = pPoint0;
			}
			else
			{
				pPoint = pPoint1;
			}
		}
		else
		{
			if ( pPoint0 )
			{
				pPoint = pPoint0;
			}
			else
			{
				if ( pPoint1 )
				{
					pPoint = pPoint1;
				}
				else
				{
					pPoint = pRawStr + rawStrLen;
				}
			}
		}

		while ( ( pFirst < pPoint - 1 ) && ( ( *pFirst < L'1' ) || ( *pFirst > L'9' ) ) )
		{
			++pFirst;
		}

		while ( ( ( pLast > pPoint + PREFERRED_CURRENCY_DECIMAL_PLACES ) && ( ( *pLast < L'1' ) || ( *pLast > L'9' ) ) ) || ( ( pLast > pPoint ) && !*pLast ) )
		{
			--pLast;
		}

		if ( *pLast && pLast[ 1 ] )
		{
			pLast[ 1 ] = L'\0';
		}

		if ( pFirst == pPoint )
		{
			fixedString = L"0";
		}
		else
		{
			fixedString = L"";
		}

		fixedString += pFirst;

		if ( !*pPoint )
		{
			fixedString += L".";
		}

		numPlaces = pLast - pPoint;
	}
	else
	{
		fixedString = L"0.";
		numPlaces = 0;
	}

	while ( numPlaces < PREFERRED_CURRENCY_DECIMAL_PLACES )
	{
		fixedString += L"0";
		++numPlaces;
	}
}


ECryLobbyError CCryGameSpyDLCStore::GetImageNameFromImageID( CryDLCImageID imageID, CryStringT< wchar_t >* pImageName )
{
	ECryLobbyError	result = eCLE_InvalidParam;

	if ( pImageName && imageID )
	{
		*pImageName = static_cast< SCryGameSpyDLCImageID* >( imageID.get() )->name;
		result = eCLE_Success;
	}

	return result;
}


#endif // USE_CRYLOBBY_GAMESPY
